diff --git a/AUTHORS b/AUTHORS
index f1773d0..251be5a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -138,6 +138,7 @@
 Arnaud Renevier <a.renevier@samsung.com>
 Arpita Bahuguna <a.bah@samsung.com>
 Arthur Lussos <developer0420@gmail.com>
+Artin Lindqvist <artin.lindqvist.chromium@gmail.com>
 Artur Akerberg <artur.aker@gmail.com>
 Arun Kulkarni <kulkarni.a@samsung.com>
 Arun Kumar <arun87.kumar@samsung.com>
@@ -764,7 +765,7 @@
 Lukas Lihotzki <lukas@lihotzki.de>
 Lukasz Krakowiak <lukasz.krakowiak@mobica.com>
 Luke Inman-Semerau <luke.semerau@gmail.com>
-Luke Seunghoe Gu <gulukesh@gmail.com>
+Luke Gu <gulukesh@gmail.com>
 Luke Zarko <lukezarko@gmail.com>
 Luoxi Pan <l.panpax@gmail.com>
 Lu Yahan <yahan@iscas.ac.cn>
diff --git a/DEPS b/DEPS
index 6d66a596..97c34429 100644
--- a/DEPS
+++ b/DEPS
@@ -299,15 +299,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'e09e7abe45968482bcb1a1fa4460e84252a675e3',
+  'skia_revision': '2e7bee153d7841be4da26b30dc878221671408c5',
   # 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': '692feefd89543e0f199fd28b2edb736ada3bbde4',
+  'v8_revision': 'fe24d473144bbc62073f1f0e544e5e69a29e554b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'f524e4b8a2448ec919d3a1c4d64396fc5b56a1bc',
+  'angle_revision': '8875ba4e4cd28a8a08d51f60ea8bc967cb26659d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -374,7 +374,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': '27639d495e1cec411073bc82ba1fe368ce0ca89a',
+  'crossbench_revision': '0941f19d9b1bab30137d9fcad6ee2ee44d338913',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -426,7 +426,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': '9efc12d4af06a27468640a54dbe2a121667dad11',
+  'dawn_revision': '35df626efa2ac803d9a578594914d26dc4f27ab6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -462,7 +462,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.
-  'cros_components_revision': 'e4973afdd5c3715ae56cfcf94c07f61f40ddcca7',
+  'cros_components_revision': '28731ebe744897c527854c2acdcd2f89f1496384',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -764,7 +764,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    'bb1015626a34c0c28351beceb09fd9996485b835',
+    'fb4c24b186737efcb3eefadd72ca483fa7c83e45',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -773,7 +773,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'dc932298481308087b192deca693e210bd11cd01',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'dbea0d824daabf9ab39bdf5c33b80f8dc1bcccf5',
       'condition': 'checkout_ios',
   },
 
@@ -863,7 +863,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': '5WbUil4KanfX4HN2rMFZwLHg0y6GiUTM3F3g6zGqMaAC',
+          'version': 'hSJK7BIhg134I-atbtbh1PeQb36ZHLoJaBpw63atjqcC',
         },
       ],
       'dep_type': 'cipd',
@@ -874,7 +874,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'HK8Sx9zMLY0YK0i3TaCoIVYKzqCmEsUuQvJDkoV10KYC',
+          'version': 'MyjKKm3_Eu20R_JPzWADp2nhjBMSfR1LqE22nnklfGoC',
         },
       ],
       'dep_type': 'cipd',
@@ -885,7 +885,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'GoOkuqQKE1qa6a0uZ5xURpZuf9_VVCxC7brsZY5IwlgC',
+          'version': 'n3nZKPUmtU03AFVClGwDuUutlEgjEiYzq8BuPt9wVE0C',
         },
       ],
       'dep_type': 'cipd',
@@ -1024,7 +1024,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/lint',
-               'version': 'MSpv-kFDDSPO0SY0dLdHegUJcJT1Yy8cL9r3vlAZ9vkC',
+               'version': 'NrP_GizJsQ_kr9O0WQlncRx1xdicjU4BFHi9pLPeTSIC',
           },
       ],
       'condition': 'checkout_android',
@@ -1035,7 +1035,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': 'EbRaK62t9grqlZqL-JTd_zwM4t1u9fm1x4c2rLE0cqQC',
+               'version': 'saMCpz15quEEWToMloh-A_rMqC0WSdJlyYTFvwAd840C',
           },
       ],
       'condition': 'checkout_android',
@@ -1174,7 +1174,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '11fe1b6bc0051aacc08c9833c81e18e1bb25d876',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4038ac8bd8f5b9fca8e0d344cf93216b36d36f3e',
       'condition': 'checkout_chromeos',
   },
 
@@ -1206,13 +1206,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9144b67c7f037577516ebdfe220997560bd01e14',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '03b4ba83f3be6bf61d87f13ce4b73eaa8d1e2c0f',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '7142b2c0cc3092a21934cf9465ca43edb5b2ae4c',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '121754bf3a97650be9bc188355ed80b667bc4bad',
     'condition': 'checkout_src_internal',
   },
 
@@ -1684,7 +1684,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a92aaa264399b0a4dc2d8744d98edcc3d7438331',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '7c5fe42e47b51d5f745aa543a76b091bf3e0daa6',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1869,7 +1869,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '6c8361e98f1daba65902f5e2fc1297893ac14b67',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ba3a1e2c26a81f2df683b43284447f5905416a41',
+    Var('webrtc_git') + '/src.git' + '@' + '419b6fcd080a7c6d50180c892f8a30581ccf9d34',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index c2abb03..baf14c2 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -661,9 +661,9 @@
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/component_updater/android:embedded_component_loader_java",
     "//components/content_capture/android:java",
+    "//components/content_relationship_verification/android:java",
     "//components/crash/android:handler_java",
     "//components/crash/android:java",
-    "//components/digital_asset_links/android:java",
     "//components/embedder_support/android:util_java",
     "//components/embedder_support/android:web_contents_delegate_java",
     "//components/embedder_support/android/metrics:java",
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index b688485..a8a4fab 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -237,10 +237,10 @@
     "//components/url_matcher",
 
     # Called via JNI in CrashpadMain
+    "//components/content_relationship_verification",
     "//components/crash/android:crashpad_main",
     "//components/crash/content/browser",
     "//components/crash/core/app",
-    "//components/digital_asset_links",
     "//components/embedder_support/android:web_contents_delegate",
     "//components/embedder_support/android/metrics",
     "//components/google/core/common",
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index e9da0d8..cd87e45 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -15,9 +15,9 @@
   "+components/cdm/browser",
   "+components/component_updater/android",
   "+components/component_updater/installer_policies",
+  "+components/content_relationship_verification",
   "+components/crash/content/browser",
   "+components/crash/core",
-  "+components/digital_asset_links",
   "+components/download/public/common",
   "+components/embedder_support",
   "+components/favicon_base",
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index bc4b7ff..bb25951 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -601,8 +601,9 @@
            features::kWebViewRestrictSensitiveContent))) {
     auto* origin_verification_bridge =
         AwOriginVerificationSchedulerBridge::GetInstance();
-    result.push_back(digital_asset_links::BrowserURLLoaderThrottle::Create(
-        origin_verification_bridge));
+    result.push_back(
+        content_relationship_verification::BrowserURLLoaderThrottle::Create(
+            origin_verification_bridge));
   }
 
   result.push_back(safe_browsing::BrowserURLLoaderThrottle::Create(
diff --git a/android_webview/browser/aw_origin_verification_scheduler_bridge.h b/android_webview/browser/aw_origin_verification_scheduler_bridge.h
index 26a2d5d..e1b74bb 100644
--- a/android_webview/browser/aw_origin_verification_scheduler_bridge.h
+++ b/android_webview/browser/aw_origin_verification_scheduler_bridge.h
@@ -8,13 +8,13 @@
 #include "base/functional/callback.h"
 #include "base/no_destructor.h"
 
-#include "components/digital_asset_links/browser_url_loader_throttle.h"
+#include "components/content_relationship_verification/browser_url_loader_throttle.h"
 
 namespace android_webview {
 using OriginVerifierCallback = base::OnceCallback<void(bool /*verified*/)>;
 
 class AwOriginVerificationSchedulerBridge
-    : public digital_asset_links::BrowserURLLoaderThrottle::
+    : public content_relationship_verification::BrowserURLLoaderThrottle::
           OriginVerificationSchedulerBridge {
  public:
   static AwOriginVerificationSchedulerBridge* GetInstance();
diff --git a/android_webview/java/DEPS b/android_webview/java/DEPS
index d756283..bf57d2f 100644
--- a/android_webview/java/DEPS
+++ b/android_webview/java/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
   "+components/autofill/android/java",
-  "+components/digital_asset_links/android/java",
+  "+components/content_relationship_verification/android/java",
   "+components/embedder_support/android/java",
   "+components/embedder_support/android/metrics/java",
   "+components/navigation_interception/android/java",
diff --git a/android_webview/java/src/org/chromium/android_webview/AwOriginVerificationScheduler.java b/android_webview/java/src/org/chromium/android_webview/AwOriginVerificationScheduler.java
index 95533ebf..cdb5de2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwOriginVerificationScheduler.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwOriginVerificationScheduler.java
@@ -11,9 +11,9 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
-import org.chromium.components.digital_asset_links.OriginVerificationScheduler;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifierHelper;
+import org.chromium.components.content_relationship_verification.OriginVerificationScheduler;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifierHelper;
 import org.chromium.components.embedder_support.util.Origin;
 
 import java.util.Set;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwOriginVerifier.java b/android_webview/java/src/org/chromium/android_webview/AwOriginVerifier.java
index 64a68dc..031f4ab 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwOriginVerifier.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwOriginVerifier.java
@@ -6,8 +6,8 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.Relationship;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.Relationship;
 import org.chromium.components.embedder_support.util.Origin;
 
 import java.util.List;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwVerificationResultStore.java b/android_webview/java/src/org/chromium/android_webview/AwVerificationResultStore.java
index c0480e8..26aef7f 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwVerificationResultStore.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwVerificationResultStore.java
@@ -4,7 +4,7 @@
 
 package org.chromium.android_webview;
 
-import org.chromium.components.digital_asset_links.VerificationResultStore;
+import org.chromium.components.content_relationship_verification.VerificationResultStore;
 
 import java.util.Collections;
 import java.util.HashSet;
diff --git a/android_webview/junit/DEPS b/android_webview/junit/DEPS
index 8c66cdd2..800c5ab 100644
--- a/android_webview/junit/DEPS
+++ b/android_webview/junit/DEPS
@@ -1,4 +1,4 @@
 include_rules = [
-    "+components/digital_asset_links/android/java",
+    "+components/content_relationship_verification/android/java",
     "+content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestThreadUtils.java",
 ]
\ No newline at end of file
diff --git a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwOriginVerifierTest.java b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwOriginVerifierTest.java
index 3f2b824..df189b1 100644
--- a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwOriginVerifierTest.java
+++ b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwOriginVerifierTest.java
@@ -28,11 +28,11 @@
 import org.chromium.android_webview.AwVerificationResultStore;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
-import org.chromium.components.digital_asset_links.OriginVerifierJni;
-import org.chromium.components.digital_asset_links.OriginVerifierUnitTestSupport;
-import org.chromium.components.digital_asset_links.RelationshipCheckResult;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifierJni;
+import org.chromium.components.content_relationship_verification.OriginVerifierUnitTestSupport;
+import org.chromium.components.content_relationship_verification.RelationshipCheckResult;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 05e5fdb0..6f2510b 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -264,7 +264,7 @@
     "//components/component_updater/android:embedded_component_loader_java",
     "//components/content_capture/android:java",
     "//components/content_capture/android/test_support:java",
-    "//components/digital_asset_links/android:java",
+    "//components/content_relationship_verification/android:java",
     "//components/embedder_support/android:util_java",
     "//components/embedder_support/android:web_contents_delegate_java",
     "//components/embedder_support/android/metrics:java",
@@ -704,9 +704,9 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//components/component_updater/android:embedded_component_loader_java",
-    "//components/digital_asset_links:java",
-    "//components/digital_asset_links/android:java",
-    "//components/digital_asset_links/android:junit_test_support",
+    "//components/content_relationship_verification:java",
+    "//components/content_relationship_verification/android:java",
+    "//components/content_relationship_verification/android:junit_test_support",
     "//components/embedder_support/android:util_java",
     "//content/public/android:content_full_java",
     "//content/public/test/android:content_java_test_support",
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 9c8853f..82a3558e 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -769,8 +769,6 @@
     "login/ui/scrollable_users_list_view.h",
     "login/ui/smart_lock_auth_factor_model.cc",
     "login/ui/smart_lock_auth_factor_model.h",
-    "login/ui/system_label_button.cc",
-    "login/ui/system_label_button.h",
     "login/ui/user_switch_flip_animation.cc",
     "login/ui/user_switch_flip_animation.h",
     "login/ui/views_utils.cc",
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index adff1c0..5341705 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -427,12 +427,8 @@
     if (!base::DirectoryExists(file_path.DirName()))
       base::CreateDirectory(file_path.DirName());
 
-    int data_size = static_cast<int>(json_string.size());
-    int bytes_written =
-        base::WriteFile(file_path, json_string.data(), data_size);
-    if (bytes_written != data_size) {
-      LOG(ERROR) << " Wrote " << bytes_written << " byte(s) instead of "
-                 << data_size << " to " << file_path.value();
+    if (!base::WriteFile(file_path, json_string)) {
+      LOG(ERROR) << "Writing to " << file_path.value() << " failed.";
       return false;
     }
     return true;
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc
index 116e2c2..a00c37be 100644
--- a/ash/ambient/ambient_controller.cc
+++ b/ash/ambient/ambient_controller.cc
@@ -577,7 +577,9 @@
 
   close_widgets_immediately_ = immediately;
   ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kClosed);
-  Shell::Get()->cursor_manager()->ShowCursor();
+  if (!Shell::Get()->IsInTabletMode()) {
+    Shell::Get()->cursor_manager()->ShowCursor();
+  }
 }
 
 void AmbientController::ToggleInSessionUi() {
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 5437a6af..54facc8 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -375,6 +375,10 @@
     case RecordingStatus::kLowDriveFsQuota:
       RecordEndRecordingReason(EndRecordingReason::kLowDriveFsQuota);
       break;
+    case RecordingStatus::kVideoEncoderReconfigurationFailure:
+      RecordEndRecordingReason(
+          EndRecordingReason::kVideoEncoderReconfigurationFailure);
+      break;
   }
 }
 
diff --git a/ash/capture_mode/capture_mode_metrics.h b/ash/capture_mode/capture_mode_metrics.h
index a342f44..3052dbb 100644
--- a/ash/capture_mode/capture_mode_metrics.h
+++ b/ash/capture_mode/capture_mode_metrics.h
@@ -36,7 +36,8 @@
   kVideoEncodingError,
   kProjectorTranscriptionError,
   kLowDriveFsQuota,
-  kMaxValue = kLowDriveFsQuota,
+  kVideoEncoderReconfigurationFailure,
+  kMaxValue = kVideoEncoderReconfigurationFailure,
 };
 
 // Enumeration of capture bar buttons that can be pressed while in capture mode.
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index 39a49c3c..dc63b424 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -1169,8 +1169,7 @@
     return;
   }
 
-  shill_network_properties_[service_path] =
-      base::Value(std::move(*shill_properties));
+  shill_network_properties_[service_path] = std::move(*shill_properties);
 
   // Get patchpanel devices and update active networks.
   ash::PatchPanelClient::Get()->GetDevices(base::BindOnce(
@@ -1209,15 +1208,14 @@
 void ArcNetHostImpl::StartLohs(mojom::LohsConfigPtr config,
                                StartLohsCallback callback) {
   NET_LOG(USER) << "Starting LOHS";
-  base::Value dict(base::Value::Type::DICT);
+  base::Value::Dict dict;
 
   if (config->hexssid.empty()) {
     NET_LOG(ERROR) << "Cannot create local only hotspot without hex ssid";
     std::move(callback).Run(arc::mojom::LohsStatus::kErrorInvalidArgument);
     return;
   }
-  dict.GetDict().Set(shill::kTetheringConfSSIDProperty,
-                     base::Value(config->hexssid));
+  dict.Set(shill::kTetheringConfSSIDProperty, config->hexssid);
 
   if (config->band != arc::mojom::WifiBand::k2Ghz) {
     // TODO(b/257880335): Support 5Ghz band as well
@@ -1226,8 +1224,7 @@
     std::move(callback).Run(arc::mojom::LohsStatus::kErrorInvalidArgument);
     return;
   }
-  dict.GetDict().Set(shill::kTetheringConfBandProperty,
-                     base::Value(shill::kBand2GHz));
+  dict.Set(shill::kTetheringConfBandProperty, shill::kBand2GHz);
 
   if (config->security_type != arc::mojom::SecurityType::WPA_PSK) {
     NET_LOG(ERROR) << "Unsupported security for LOHS: " << config->security_type
@@ -1240,16 +1237,14 @@
     std::move(callback).Run(arc::mojom::LohsStatus::kErrorInvalidArgument);
     return;
   }
-  dict.GetDict().Set(shill::kTetheringConfSecurityProperty,
-                     base::Value(shill::kSecurityWpa2));
-  dict.GetDict().Set(shill::kTetheringConfPassphraseProperty,
-                     base::Value(config->passphrase.value()));
+  dict.Set(shill::kTetheringConfSecurityProperty, shill::kSecurityWpa2);
+  dict.Set(shill::kTetheringConfPassphraseProperty, config->passphrase.value());
 
   NET_LOG(USER) << "Set Shill Manager property: " << shill::kLOHSConfigProperty
                 << ": " << dict;
   auto callback_split = base::SplitOnceCallback(std::move(callback));
   ash::ShillManagerClient::Get()->SetProperty(
-      shill::kLOHSConfigProperty, dict,
+      shill::kLOHSConfigProperty, base::Value(std::move(dict)),
       base::BindOnce(&SetLohsConfigPropertySuccessCallback,
                      std::move(callback_split.first)),
       base::BindOnce(&SetLohsConfigPropertyFailureCallback,
diff --git a/ash/components/arc/net/arc_net_host_impl.h b/ash/components/arc/net/arc_net_host_impl.h
index 7040838c..e8e12af 100644
--- a/ash/components/arc/net/arc_net_host_impl.h
+++ b/ash/components/arc/net/arc_net_host_impl.h
@@ -141,15 +141,15 @@
   // ARC VPN connection.
   std::string LookupArcVpnServicePath();
 
-  // Convert a vector of strings, |string_list|, to a base::Value
-  // that can be added to an ONC dictionary.  This is used for fields
-  // like NameServers, SearchDomains, etc.
+  // Convert a vector of strings, |string_list|, to a base::Value::List that can
+  // be added to an ONC dictionary.  This is used for fields like NameServers,
+  // SearchDomains, etc.
   base::Value::List TranslateStringListToValue(
       const std::vector<std::string>& string_list);
 
-  // Convert a vector of uint64_t, |long_list|, to a base::Value of type list
-  // that can be passed to shill. This is because 64-bit integer values are not
-  // supported for base::Value.
+  // Convert a vector of uint64_t, |long_list|, to a base::Value::List that can
+  // be passed to shill. This is because 64-bit integer values are not supported
+  // for base::Value.
   // The translated values will be a list of decimal string and not a single
   // string.
   base::Value::List TranslateLongListToStringValue(
@@ -165,14 +165,14 @@
   // Ask Android to disconnect any VPN app that is currently connected.
   void DisconnectArcVpn();
 
-  // Translate EAP credentials to base::Value dictionary and run |callback|.
+  // Translate EAP credentials to base::Value::Dict and run |callback|.
   // If it is necessary to import certificates this method will asynchronously
   // import them and run |callback| afterwards.
   void TranslateEapCredentialsToDict(
       mojom::EapCredentialsPtr cred,
       base::OnceCallback<void(base::Value::Dict)> callback);
 
-  // Synchronously translate EAP credentials to base::Value dictionary with
+  // Synchronously translate EAP credentials to base::Value::Dict with
   // empty or imported certificate and slot ID. |callback| is then run with
   // the translated values.
   void TranslateEapCredentialsToDictWithCertID(
@@ -181,14 +181,14 @@
       const absl::optional<std::string>& cert_id,
       const absl::optional<int>& slot_id);
 
-  // Translate passpoint credentials to base::Value dictionary and run
+  // Translate passpoint credentials to base::Value::Dict and run
   // |callback|. If it is necessary to import certificates this method will
   // asynchronously import them and run |callback| afterwards.
   void TranslatePasspointCredentialsToDict(
       mojom::PasspointCredentialsPtr cred,
       base::OnceCallback<void(base::Value::Dict)> callback);
 
-  // Synchronously translate passpoint credentials to base::Value dictionary
+  // Synchronously translate passpoint credentials to base::Value::Dict
   // with EAP fields translated inside |dict|. |callback| is then run with
   // the translated values.
   void TranslatePasspointCredentialsToDictWithEapTranslated(
@@ -229,7 +229,7 @@
   // state changes.
   bool observing_network_state_ = false;
   // Cached shill properties for all active networks, keyed by Service path.
-  std::map<std::string, base::Value> shill_network_properties_;
+  std::map<std::string, base::Value::Dict> shill_network_properties_;
 
   std::string cached_service_path_;
   std::string cached_guid_;
diff --git a/ash/components/arc/net/arc_net_utils.cc b/ash/components/arc/net/arc_net_utils.cc
index fd9a81020..61a136b 100644
--- a/ash/components/arc/net/arc_net_utils.cc
+++ b/ash/components/arc/net/arc_net_utils.cc
@@ -41,19 +41,12 @@
 // Parses a shill IPConfig dictionary and adds the relevant fields to
 // the given |network| NetworkConfiguration object.
 void AddIpConfiguration(arc::mojom::NetworkConfiguration* network,
-                        const base::Value* shill_ipconfig) {
-  const base::Value::Dict* shill_ipconfig_dict = shill_ipconfig->GetIfDict();
-  if (!shill_ipconfig_dict) {
-    return;
-  }
-
+                        const base::Value::Dict* shill_ipconfig) {
   // Only set the IP address and gateway if both are defined and non empty.
-  const auto* address =
-      shill_ipconfig_dict->FindString(shill::kAddressProperty);
-  const auto* gateway =
-      shill_ipconfig_dict->FindString(shill::kGatewayProperty);
+  const auto* address = shill_ipconfig->FindString(shill::kAddressProperty);
+  const auto* gateway = shill_ipconfig->FindString(shill::kGatewayProperty);
   const int prefixlen =
-      shill_ipconfig_dict->FindInt(shill::kPrefixlenProperty).value_or(0);
+      shill_ipconfig->FindInt(shill::kPrefixlenProperty).value_or(0);
   if (address && !address->empty() && gateway && !gateway->empty()) {
     if (prefixlen < 64) {
       network->host_ipv4_prefix_length = prefixlen;
@@ -69,7 +62,7 @@
   // If the user has overridden DNS with the "Google nameservers" UI options,
   // the kStaticIPConfigProperty object will be empty except for DNS addresses.
   if (const auto* dns_list =
-          shill_ipconfig_dict->FindList(shill::kNameServersProperty)) {
+          shill_ipconfig->FindList(shill::kNameServersProperty)) {
     for (const auto& dns_value : *dns_list) {
       const std::string& dns = dns_value.GetString();
       if (dns.empty()) {
@@ -87,19 +80,19 @@
   }
 
   if (const auto* domains =
-          shill_ipconfig_dict->FindList(shill::kSearchDomainsProperty)) {
+          shill_ipconfig->FindList(shill::kSearchDomainsProperty)) {
     for (const auto& domain : *domains) {
       network->host_search_domains->push_back(domain.GetString());
     }
   }
 
-  const int mtu = shill_ipconfig_dict->FindInt(shill::kMtuProperty).value_or(0);
+  const int mtu = shill_ipconfig->FindInt(shill::kMtuProperty).value_or(0);
   if (mtu > 0) {
     network->host_mtu = mtu;
   }
 
   if (const auto* include_routes_list =
-          shill_ipconfig_dict->FindList(shill::kIncludedRoutesProperty)) {
+          shill_ipconfig->FindList(shill::kIncludedRoutesProperty)) {
     for (const auto& include_routes_value : *include_routes_list) {
       const std::string& include_route = include_routes_value.GetString();
       if (!include_route.empty()) {
@@ -109,7 +102,7 @@
   }
 
   if (const auto* exclude_routes_list =
-          shill_ipconfig_dict->FindList(shill::kExcludedRoutesProperty)) {
+          shill_ipconfig->FindList(shill::kExcludedRoutesProperty)) {
     for (const auto& exclude_routes_value : *exclude_routes_list) {
       const std::string& exclude_route = exclude_routes_value.GetString();
       if (!exclude_route.empty()) {
@@ -154,7 +147,7 @@
 
 arc::mojom::NetworkConfigurationPtr TranslateNetworkProperties(
     const ash::NetworkState* network_state,
-    const base::Value* shill_dict) {
+    const base::Value::Dict* shill_dict) {
   auto mojo = arc::mojom::NetworkConfiguration::New();
   // Initialize optional array fields to avoid null guards both here and in ARC.
   mojo->host_ipv6_global_addresses = std::vector<std::string>();
@@ -172,7 +165,7 @@
   mojo->type = TranslateNetworkType(network_state->type());
   mojo->is_metered =
       shill_dict &&
-      shill_dict->FindBoolPath(shill::kMeteredProperty).value_or(false);
+      shill_dict->FindBool(shill::kMeteredProperty).value_or(false);
 
   // IP configuration data is added from the properties of the underlying shill
   // Device and shill Service attached to the Device. Device properties are
@@ -192,15 +185,20 @@
   if (const auto* device =
           GetStateHandler()->GetDeviceState(network_state->device_path())) {
     mojo->network_interface = device->interface();
-    for (const auto kv : device->ip_configs()) {
-      AddIpConfiguration(mojo.get(), &kv.second);
+    for (const auto [key, value] : device->ip_configs()) {
+      if (value.is_dict()) {
+        AddIpConfiguration(mojo.get(), &value.GetDict());
+      }
     }
   }
 
   if (shill_dict) {
     for (const auto* property :
          {shill::kStaticIPConfigProperty, shill::kSavedIPConfigProperty}) {
-      AddIpConfiguration(mojo.get(), shill_dict->GetDict().Find(property));
+      const base::Value::Dict* config = shill_dict->FindDict(property);
+      if (config) {
+        AddIpConfiguration(mojo.get(), config);
+      }
     }
   }
 
@@ -215,9 +213,10 @@
     mojo->wifi->rssi = network_state->rssi();
     if (shill_dict) {
       mojo->wifi->hidden_ssid =
-          shill_dict->FindBoolPath(shill::kWifiHiddenSsid).value_or(false);
+          shill_dict->FindBoolByDottedPath(shill::kWifiHiddenSsid)
+              .value_or(false);
       const auto* fqdn =
-          shill_dict->FindStringPath(shill::kPasspointFQDNProperty);
+          shill_dict->FindStringByDottedPath(shill::kPasspointFQDNProperty);
       if (fqdn && !fqdn->empty()) {
         mojo->wifi->fqdn = *fqdn;
       }
@@ -360,7 +359,7 @@
 std::vector<arc::mojom::NetworkConfigurationPtr> TranslateNetworkStates(
     const std::string& arc_vpn_path,
     const ash::NetworkStateHandler::NetworkStateList& network_states,
-    const std::map<std::string, base::Value>& shill_network_properties,
+    const std::map<std::string, base::Value::Dict>& shill_network_properties,
     const std::vector<patchpanel::NetworkDevice>& devices) {
   // Move the devices vector to a map keyed by its physical interface name in
   // order to avoid multiple loops. The map also filters non-ARC devices.
@@ -391,7 +390,7 @@
     }
 
     const auto it = shill_network_properties.find(network_path);
-    const auto* shill_dict =
+    const base::Value::Dict* shill_dict =
         (it != shill_network_properties.end()) ? &it->second : nullptr;
     auto network = TranslateNetworkProperties(state, shill_dict);
     network->is_default_network = state == GetStateHandler()->DefaultNetwork();
diff --git a/ash/components/arc/net/arc_net_utils.h b/ash/components/arc/net/arc_net_utils.h
index 2896fe2..12c376f 100644
--- a/ash/components/arc/net/arc_net_utils.h
+++ b/ash/components/arc/net/arc_net_utils.h
@@ -22,10 +22,10 @@
 
 // Translates a shill network state into a mojo NetworkConfigurationPtr.
 // This get network properties from NetworkState and populating the
-// corresponding fields defined in NetworkConiguration in mojo.
+// corresponding fields defined in NetworkConfiguration in mojo.
 arc::mojom::NetworkConfigurationPtr TranslateNetworkProperties(
     const ash::NetworkState* network_state,
-    const base::Value* shill_dict);
+    const base::Value::Dict* shill_dict);
 
 // Translates a mojo EapMethod into a shill EAP method.
 std::string TranslateEapMethod(arc::mojom::EapMethod method);
@@ -55,7 +55,7 @@
 std::vector<arc::mojom::NetworkConfigurationPtr> TranslateNetworkStates(
     const std::string& arc_vpn_path,
     const ash::NetworkStateHandler::NetworkStateList& network_states,
-    const std::map<std::string, base::Value>& shill_network_properties,
+    const std::map<std::string, base::Value::Dict>& shill_network_properties,
     const std::vector<patchpanel::NetworkDevice>& devices);
 
 }  // namespace arc::net_utils
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 16c5417..62f9a63 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -99,6 +99,11 @@
              "ChromeOSAmbientModeThrottleAnimation",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Controls whether the logic for managed screensaver is enabled or not.
+BASE_FEATURE(kAmbientModeManagedScreensaver,
+             "ChromeOSAmbientModeManagedScreensaver",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kApnRevamp, "ApnRevamp", base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kAppCollectionFolderRefresh,
@@ -1013,6 +1018,11 @@
 // Enable glanceables on login.
 BASE_FEATURE(kGlanceables, "Glanceables", base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables glanceables on time management surface.
+BASE_FEATURE(kGlanceablesV2,
+             "GlanceablesV2",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables the Gaia reauth endpoint.
 BASE_FEATURE(kGaiaReauthEndpoint,
              "GaiaReauthEndpoint",
@@ -2309,6 +2319,10 @@
   return base::FeatureList::IsEnabled(kAmbientModeDevUseProdFeature);
 }
 
+bool IsAmbientModeManagedScreensaverEnabled() {
+  return base::FeatureList::IsEnabled(kAmbientModeManagedScreensaver);
+}
+
 bool IsAmbientModePhotoPreviewEnabled() {
   return base::FeatureList::IsEnabled(kAmbientModePhotoPreviewFeature);
 }
@@ -2641,6 +2655,10 @@
   return base::FeatureList::IsEnabled(kGlanceables);
 }
 
+bool AreGlanceablesV2Enabled() {
+  return base::FeatureList::IsEnabled(kGlanceablesV2);
+}
+
 bool IsHibernateEnabled() {
   return base::FeatureList::IsEnabled(kHibernate);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index e0364b4..27d9c63 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -32,6 +32,8 @@
 BASE_DECLARE_FEATURE(kAmbientModePhotoPreviewFeature);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kAmbientModeThrottleAnimation);
+COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kAmbientModeManagedScreensaver);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kApnRevamp);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kAppCollectionFolderRefresh);
@@ -302,7 +304,9 @@
 BASE_DECLARE_FEATURE(kGalleryAppPdfEditNotification);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::FeatureParam<std::string> kGalleryAppPdfEditNotificationText;
+// TODO(b/270948434): Remove `kGlanceables`.
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGlanceables);
+COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGlanceablesV2);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGaiaReauthEndpoint);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGameDashboard);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGamepadVibration);
@@ -629,6 +633,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsEapDefaultCasWithoutSubjectVerificationAllowed();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModeDevUseProdEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModeManagedScreensaverEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModePhotoPreviewEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModeThrottleAnimationEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsApnRevampEnabled();
@@ -720,6 +725,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsGifRecordingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsGifRenderingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreGlanceablesEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool AreGlanceablesV2Enabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHibernateEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideArcMediaNotificationsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideShelfControlsInTabletModeEnabled();
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index e861cb6..b0a75eb 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -130,6 +130,20 @@
 const char kInputNoiseCancellationEnabled[] =
     "ash.input_noise_cancellation_enabled";
 
+// The name of an integer pref that counts the number of times we have shown
+// the multitask menu education nudge.
+const char kMultitaskMenuNudgeClamshellShownCount[] =
+    "ash.wm_nudge.multitask_nudge_count";
+const char kMultitaskMenuNudgeTabletShownCount[] =
+    "cros.wm_nudge.tablet_multitask_nudge_count";
+
+// The name of a time pref that stores the time we last showed the multitask
+// menu education nudge.
+const char kMultitaskMenuNudgeClamshellLastShown[] =
+    "ash.wm_nudge.multitask_menu_nudge_last_shown";
+const char kMultitaskMenuNudgeTabletLastShown[] =
+    "cros.wm_nudge.tablet_multitask_nudge_last_shown";
+
 // The following SAML-related prefs are not settings that the domain admin can
 // set, but information that the SAML Identity Provider can send us:
 
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 95ffa7ea..fdedb7be 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -57,6 +57,14 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kInputNoiseCancellationEnabled[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kMultitaskMenuNudgeClamshellShownCount[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kMultitaskMenuNudgeTabletShownCount[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kMultitaskMenuNudgeClamshellLastShown[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kMultitaskMenuNudgeTabletLastShown[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSamlPasswordModifiedTime[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSamlPasswordExpirationTime[];
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index c4222ef..aad1cf5 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -30,7 +30,6 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/note_action_launch_button.h"
 #include "ash/login/ui/scrollable_users_list_view.h"
-#include "ash/login/ui/system_label_button.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/media/media_controller_impl.h"
 #include "ash/metrics/user_metrics_recorder.h"
@@ -49,6 +48,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
 #include "ash/style/ash_color_provider.h"
+#include "ash/style/pill_button.h"
 #include "ash/system/model/enterprise_domain_model.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/power/power_button_controller.h"
@@ -2454,10 +2454,10 @@
   MakeSectionBold(label.get(), error_text, bold_start, bold_length);
   label->SetAutoColorReadabilityEnabled(false);
 
-  auto learn_more_button = std::make_unique<SystemLabelButton>(
+  auto learn_more_button = std::make_unique<PillButton>(
       base::BindRepeating(&LockContentsView::LearnMoreButtonPressed,
                           base::Unretained(this)),
-      l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE), /*multiline=*/true);
+      l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
 
   auto container = std::make_unique<NonAccessibleView>(kAuthErrorContainerName);
   auto* container_layout =
@@ -2475,11 +2475,10 @@
     if (screen_type_ == LockScreen::ScreenType::kLogin &&
         Shell::Get()->session_controller()->GetSessionState() !=
             session_manager::SessionState::LOGIN_SECONDARY) {
-      auto recover_user_button = std::make_unique<SystemLabelButton>(
+      auto recover_user_button = std::make_unique<PillButton>(
           base::BindRepeating(&LockContentsView::RecoverUserButtonPressed,
                               base::Unretained(this)),
-          l10n_util::GetStringUTF16(IDS_ASH_LOGIN_RECOVER_USER_BUTTON),
-          /*multiline=*/true);
+          l10n_util::GetStringUTF16(IDS_ASH_LOGIN_RECOVER_USER_BUTTON));
 
       container->AddChildView(std::move(recover_user_button));
     }
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index c260793..b4259a4 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -26,7 +26,6 @@
 #include "ash/login/ui/pin_keyboard_animation.h"
 #include "ash/login/ui/pin_request_view.h"
 #include "ash/login/ui/smart_lock_auth_factor_model.h"
-#include "ash/login/ui/system_label_button.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/smartlock_state.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -36,6 +35,7 @@
 #include "ash/style/ash_color_id.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/color_util.h"
+#include "ash/style/pill_button.h"
 #include "ash/system/model/clock_model.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/time/time_of_day.h"
@@ -1094,7 +1094,7 @@
       gfx::Insets::TLBR(kPinPasswordToggleButtonPaddingTop, 0,
                         kPinPasswordToggleButtonPaddingBottom, 0)));
   pin_password_toggle_ =
-      toggle_container->AddChildView(std::make_unique<SystemLabelButton>(
+      toggle_container->AddChildView(std::make_unique<PillButton>(
           base::BindRepeating(&LoginAuthUserView::OnSwitchButtonClicked,
                               base::Unretained(this)),
           GetPinPasswordToggleText()));
@@ -1127,7 +1127,7 @@
         l10n_util::GetStringUTF16(IDS_ASH_LOCK_SCREEN_VERIFY_ACCOUNT_MESSAGE);
   }
 
-  auto online_sign_in_button = std::make_unique<SystemLabelButton>(
+  auto online_sign_in_button = std::make_unique<PillButton>(
       base::BindRepeating(&LoginAuthUserView::OnOnlineSignInMessageTap,
                           base::Unretained(this)),
       button_message);
diff --git a/ash/login/ui/login_remove_account_dialog.cc b/ash/login/ui/login_remove_account_dialog.cc
index 7f3df9e..3f70526 100644
--- a/ash/login/ui/login_remove_account_dialog.cc
+++ b/ash/login/ui/login_remove_account_dialog.cc
@@ -4,12 +4,12 @@
 
 #include "ash/login/ui/login_remove_account_dialog.h"
 #include "ash/login/ui/non_accessible_view.h"
-#include "ash/login/ui/system_label_button.h"
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/login_types.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
+#include "ash/style/pill_button.h"
 #include "base/functional/bind.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
@@ -71,19 +71,23 @@
 }  // namespace
 
 // A system label button that dismisses its bubble dialog parent on key event.
-class RemoveUserButton : public SystemLabelButton {
+class RemoveUserButton : public PillButton {
  public:
   RemoveUserButton(PressedCallback callback, LoginRemoveAccountDialog* bubble)
-      : SystemLabelButton(std::move(callback),
-                          l10n_util::GetStringUTF16(
-                              IDS_ASH_LOGIN_POD_REMOVE_ACCOUNT_ACCESSIBLE_NAME),
-                          /*multiline=*/true),
+      : PillButton(std::move(callback),
+                   l10n_util::GetStringUTF16(
+                       IDS_ASH_LOGIN_POD_REMOVE_ACCOUNT_ACCESSIBLE_NAME)),
         bubble_(bubble) {}
 
   RemoveUserButton(const RemoveUserButton&) = delete;
   RemoveUserButton& operator=(const RemoveUserButton&) = delete;
   ~RemoveUserButton() override = default;
 
+  void SetAlert(bool alert) {
+    SetPillButtonType(alert ? PillButton::Type::kAlertWithoutIcon
+                            : PillButton::Type::kDefaultWithoutIcon);
+  }
+
  private:
   void OnKeyEvent(ui::KeyEvent* event) override {
     if (event->type() != ui::ET_KEY_PRESSED ||
@@ -233,7 +237,7 @@
   }
   if (remove_user_confirm_data_) {
     remove_user_confirm_data_->SetVisible(false);
-    remove_user_button_->SetBackgroundAndFont(/*alert_mode=*/false);
+    remove_user_button_->SetAlert(false);
     // Reset button's description to none.
     remove_user_button_->GetViewAccessibility().OverrideDescription(
         std::u16string(),
@@ -306,7 +310,7 @@
     if (management_disclosure_label_) {
       management_disclosure_label_->SetVisible(false);
     }
-    remove_user_button_->SetBackgroundAndFont(/*alert_mode=*/true);
+    remove_user_button_->SetAlert(true);
 
     Layout();
 
diff --git a/ash/login/ui/system_label_button.cc b/ash/login/ui/system_label_button.cc
deleted file mode 100644
index f42578b..0000000
--- a/ash/login/ui/system_label_button.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/login/ui/system_label_button.h"
-
-#include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/style/ash_color_provider.h"
-#include "ash/style/style_util.h"
-#include "ui/color/color_id.h"
-#include "ui/compositor/layer.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/color_utils.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/views/animation/ink_drop.h"
-#include "ui/views/controls/focus_ring.h"
-#include "ui/views/controls/highlight_path_generator.h"
-
-namespace ash {
-
-namespace {
-constexpr int kUserInfoBubbleWidth = 192;
-constexpr int kUserInfoBubbleExternalPadding = 8;
-constexpr int kSystemButtonHeight = 32;
-constexpr int kSystemButtonIconSize = 20;
-constexpr int kSystemButtonMarginTopBottomDp = 6;
-constexpr int kSystemButtonMarginLeftRightDp = 16;
-constexpr int kSystemButtonBorderRadius = 16;
-constexpr int kSystemButtonImageLabelSpacing = 8;
-constexpr int kSystemButtonMaxLabelWidthDp =
-    kUserInfoBubbleWidth - 2 * kUserInfoBubbleExternalPadding -
-    kSystemButtonIconSize - kSystemButtonImageLabelSpacing -
-    2 * kSystemButtonBorderRadius;
-
-// Default base layer used for the bubble background, above which the system
-// label button lives.
-constexpr const AshColorProvider::BaseLayerType kBubbleLayerType =
-    AshColorProvider::BaseLayerType::kTransparent80;
-
-SkPath GetSystemButtonHighlightPath(const views::View* view) {
-  gfx::Rect rect(view->GetLocalBounds());
-  return SkPath().addRoundRect(gfx::RectToSkRect(rect),
-                               kSystemButtonBorderRadius,
-                               kSystemButtonBorderRadius);
-}
-
-}  // namespace
-
-SystemLabelButton::SystemLabelButton(PressedCallback callback,
-                                     const std::u16string& text,
-                                     bool multiline)
-    : LabelButton(std::move(callback), text) {
-  SetImageLabelSpacing(kSystemButtonImageLabelSpacing);
-  if (multiline) {
-    label()->SetMultiLine(true);
-    label()->SetMaximumWidth(kSystemButtonMaxLabelWidthDp);
-  }
-  SetMinSize(gfx::Size(0, kSystemButtonHeight));
-  SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
-  SetTextSubpixelRenderingEnabled(false);
-  views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
-
-  SetFocusBehavior(FocusBehavior::ALWAYS);
-  SetInstallFocusRingOnFocus(true);
-  views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
-  views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
-                                                kSystemButtonBorderRadius);
-}
-
-void SystemLabelButton::PaintButtonContents(gfx::Canvas* canvas) {
-  cc::PaintFlags flags;
-  flags.setAntiAlias(true);
-  flags.setColor(background_color_);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  canvas->DrawPath(GetSystemButtonHighlightPath(this), flags);
-}
-
-gfx::Insets SystemLabelButton::GetInsets() const {
-  return gfx::Insets::TLBR(
-      kSystemButtonMarginTopBottomDp, kSystemButtonMarginLeftRightDp,
-      kSystemButtonMarginTopBottomDp, kSystemButtonMarginLeftRightDp);
-}
-
-void SystemLabelButton::OnThemeChanged() {
-  views::LabelButton::OnThemeChanged();
-  SetBackgroundAndFont(alert_mode_);
-}
-
-void SystemLabelButton::SetBackgroundAndFont(bool alert_mode) {
-  // Do not check if alert mode has already been set since the variable might
-  // have been initialized by default while the colors have not been set yet.
-  alert_mode_ = alert_mode;
-
-  background_color_ = AshColorProvider::Get()->GetControlsLayerColor(
-      alert_mode
-          ? AshColorProvider::ControlsLayerType::kControlBackgroundColorAlert
-          : AshColorProvider::ControlsLayerType::
-                kControlBackgroundColorInactive);
-
-  label()->SetFontList(gfx::FontList().DeriveWithWeight(
-      alert_mode ? gfx::Font::Weight::BOLD : gfx::Font::Weight::MEDIUM));
-
-  SetEnabledTextColors(AshColorProvider::Get()->GetContentLayerColor(
-      alert_mode ? AshColorProvider::ContentLayerType::kButtonLabelColorPrimary
-                 : AshColorProvider::ContentLayerType::kButtonLabelColor));
-
-  // In default mode, this won't be the exact resulting color of the button as
-  // neither |background_color_| nor the color bubble below are fully opaque.
-  // Nevertheless, the result is visually satisfying and better than without
-  // applying any background color.
-  SkColor effective_background_color = color_utils::GetResultingPaintColor(
-      background_color_,
-      AshColorProvider::Get()->GetBaseLayerColor(kBubbleLayerType));
-  StyleUtil::ConfigureInkDropAttributes(this,
-                                        StyleUtil::kBaseColor |
-                                            StyleUtil::kInkDropOpacity |
-                                            StyleUtil::kHighlightOpacity,
-                                        effective_background_color);
-}
-
-}  // namespace ash
diff --git a/ash/login/ui/system_label_button.h b/ash/login/ui/system_label_button.h
deleted file mode 100644
index 5774dabf..0000000
--- a/ash/login/ui/system_label_button.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LOGIN_UI_SYSTEM_LABEL_BUTTON_H_
-#define ASH_LOGIN_UI_SYSTEM_LABEL_BUTTON_H_
-
-#include "ash/ash_export.h"
-#include "ui/views/controls/button/label_button.h"
-
-namespace ash {
-
-// SystemLabelButton provides styled buttons with label for the login screen.
-class ASH_EXPORT SystemLabelButton : public views::LabelButton {
- public:
-  SystemLabelButton(PressedCallback callback,
-                    const std::u16string& text,
-                    bool multiline = false);
-  SystemLabelButton(const SystemLabelButton&) = delete;
-  SystemLabelButton& operator=(const SystemLabelButton&) = delete;
-  ~SystemLabelButton() override = default;
-
-  // views::LabelButton:
-  void PaintButtonContents(gfx::Canvas* canvas) override;
-  gfx::Insets GetInsets() const override;
-  void OnThemeChanged() override;
-
-  void SetBackgroundAndFont(bool alert_mode);
-
- private:
-  bool alert_mode_ = false;
-
-  // Absurd color to show the developer that background color has not been
-  // initialized properly.
-  SkColor background_color_ = SK_ColorGREEN;
-};
-
-}  // namespace ash
-
-#endif  // ASH_LOGIN_UI_SYSTEM_LABEL_BUTTON_H_
diff --git a/ash/public/cpp/input_device_settings_controller.h b/ash/public/cpp/input_device_settings_controller.h
index 66a8a5e..8d17541 100644
--- a/ash/public/cpp/input_device_settings_controller.h
+++ b/ash/public/cpp/input_device_settings_controller.h
@@ -52,6 +52,20 @@
   // Returns a list of currently connected pointing sticks and their settings.
   virtual std::vector<mojom::PointingStickPtr> GetConnectedPointingSticks() = 0;
 
+  // Returns the settings of the keyboard with a device id of `id` or nullptr if
+  // no such device exists.
+  virtual const mojom::KeyboardSettings* GetKeyboardSettings(DeviceId id) = 0;
+  // Returns the settings of the touchpad with a device id of `id` or nullptr if
+  // no such device exists.
+  virtual const mojom::TouchpadSettings* GetTouchpadSettings(DeviceId id) = 0;
+  // Returns the settings of the mouse with a device id of `id` or nullptr if
+  // no such device exists.
+  virtual const mojom::MouseSettings* GetMouseSettings(DeviceId id) = 0;
+  // Returns the settings of the pointing stick with a device id of `id` or
+  // nullptr if no such device exists.
+  virtual const mojom::PointingStickSettings* GetPointingStickSettings(
+      DeviceId id) = 0;
+
   // Configure the settings for keyboard of |id| with the provided |settings|.
   virtual void SetKeyboardSettings(DeviceId id,
                                    const mojom::KeyboardSettings& settings) = 0;
diff --git a/ash/public/cpp/projector/annotator_tool.cc b/ash/public/cpp/projector/annotator_tool.cc
index 31bc0a2..5aefbdbe 100644
--- a/ash/public/cpp/projector/annotator_tool.cc
+++ b/ash/public/cpp/projector/annotator_tool.cc
@@ -8,19 +8,10 @@
 
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/values.h"
 
 namespace ash {
 
-namespace {
-
-const char kToolColor[] = "color";
-const char kToolSize[] = "size";
-const char kToolType[] = "tool";
-
-// Returns the hex value in RGBA format.
-// For example, SK_ColorGREEN -> "00FF00FF".
-std::string ConvertColorToHexString(SkColor color) {
+std::string AnnotatorTool::GetColorHexString() const {
   uint8_t alpha = SkColorGetA(color);
   uint8_t red = SkColorGetR(color);
   uint8_t green = SkColorGetG(color);
@@ -29,24 +20,7 @@
   return base::HexEncode(bytes);
 }
 
-// Converts the RGBA hex string to ARGB, then to an SkColor.
-// For example, "000000FF" -> "SK_ColorBLACK".
-// Returns red if conversion fails.
-SkColor ConvertHexStringToColor(const std::string& rgba_hex) {
-  const size_t kHexColorLength = 8;
-  const size_t kRgbaLength = 6;
-  if (rgba_hex.length() < kHexColorLength) {
-    return SK_ColorRED;
-  }
-  uint32_t argb_color;
-  // Shift the alpha value to the front of the hex string.
-  const bool success = base::HexStringToUInt(
-      rgba_hex.substr(kRgbaLength) + rgba_hex.substr(0, kRgbaLength),
-      &argb_color);
-  return success ? argb_color : SK_ColorRED;
-}
-
-std::string ConvertToolTypeToString(AnnotatorToolType type) {
+std::string AnnotatorTool::GetToolString() const {
   switch (type) {
     case AnnotatorToolType::kMarker:
       return "marker";
@@ -59,46 +33,6 @@
   }
 }
 
-AnnotatorToolType ConvertStringToToolType(const std::string& type) {
-  if (type == "marker")
-    return AnnotatorToolType::kMarker;
-  if (type == "pen")
-    return AnnotatorToolType::kPen;
-  if (type == "highlighter")
-    return AnnotatorToolType::kHighlighter;
-  if (type == "eraser")
-    return AnnotatorToolType::kEraser;
-  NOTREACHED();
-  return AnnotatorToolType::kMarker;
-}
-
-}  // namespace
-
-// static
-AnnotatorTool AnnotatorTool::FromValue(const base::Value& value) {
-  const base::Value::Dict* dict = value.GetIfDict();
-  DCHECK(dict);
-  DCHECK(dict->Find(kToolColor));
-  DCHECK(dict->FindString(kToolColor));
-  DCHECK(dict->Find(kToolSize));
-  DCHECK(dict->FindInt(kToolSize));
-  DCHECK(dict->Find(kToolType));
-  DCHECK(dict->FindString(kToolType));
-  AnnotatorTool t;
-  t.color = ConvertHexStringToColor(*(dict->FindString(kToolColor)));
-  t.size = *(dict->FindInt(kToolSize));
-  t.type = ConvertStringToToolType(*(dict->FindString(kToolType)));
-  return t;
-}
-
-base::Value AnnotatorTool::ToValue() const {
-  base::Value::Dict val;
-  val.Set(kToolColor, ConvertColorToHexString(color));
-  val.Set(kToolSize, size);
-  val.Set(kToolType, ConvertToolTypeToString(type));
-  return base::Value(std::move(val));
-}
-
 bool AnnotatorTool::operator==(const AnnotatorTool& rhs) const {
   return rhs.color == color && rhs.size == size && rhs.type == type;
 }
diff --git a/ash/public/cpp/projector/annotator_tool.h b/ash/public/cpp/projector/annotator_tool.h
index 1b09039f..cc443c30 100644
--- a/ash/public/cpp/projector/annotator_tool.h
+++ b/ash/public/cpp/projector/annotator_tool.h
@@ -8,10 +8,6 @@
 #include "ash/public/cpp/ash_public_export.h"
 #include "third_party/skia/include/core/SkColor.h"
 
-namespace base {
-class Value;
-}  // namespace base
-
 namespace ash {
 
 // The annotator tool type.
@@ -26,8 +22,12 @@
 
 // The tool that the annotator will use.
 struct ASH_PUBLIC_EXPORT AnnotatorTool {
-  static AnnotatorTool FromValue(const base::Value& value);
-  base::Value ToValue() const;
+  // Returns the hex value in RGBA format.
+  // For example, SK_ColorGREEN -> "00FF00FF".
+  std::string GetColorHexString() const;
+
+  // Returns the tool chosen as a string.
+  std::string GetToolString() const;
 
   bool operator==(const AnnotatorTool& rhs) const;
 
diff --git a/ash/shell.h b/ash/shell.h
index 3eb57fe1..6519c2d8 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -16,9 +16,6 @@
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/system_sounds_delegate.h"
-#include "ash/system/input_device_settings/input_device_settings_controller_impl.h"
-#include "ash/system/input_device_settings/input_device_tracker.h"
-#include "ash/system/input_device_settings/keyboard_modifier_metrics_recorder.h"
 #include "ash/wm/system_modal_container_event_filter_delegate.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
@@ -150,11 +147,14 @@
 class HoldingSpaceController;
 class HumanPresenceOrientationController;
 class ImeControllerImpl;
+class InputDeviceSettingsControllerImpl;
+class InputDeviceTracker;
 class WebAuthNDialogControllerImpl;
 class KeyAccessibilityEnabler;
 class KeyboardBacklightColorController;
 class KeyboardBrightnessControlDelegate;
 class KeyboardControllerImpl;
+class KeyboardModifierMetricsRecorder;
 class LaserPointerController;
 class LocaleUpdateControllerImpl;
 class LockStateController;
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc
index 757f653..2a745d85 100644
--- a/ash/style/ash_color_mixer.cc
+++ b/ash/style/ash_color_mixer.cc
@@ -557,7 +557,10 @@
 
   mixer[ui::kColorAshOnboardingFocusRing] = {cros_tokens::kColorProminentDark};
 
-  mixer[ui::kColorAshSystemUIMenuBackground] = {kColorAshShieldAndBase80};
+  mixer[ui::kColorAshSystemUIMenuBackground] = {
+      features::IsJellyEnabled()
+          ? static_cast<ui::ColorId>(cros_tokens::kCrosSysAppBaseElevated)
+          : kColorAshShieldAndBase80};
   mixer[ui::kColorAshSystemUIMenuIcon] = {kColorAshIconColorPrimary};
   mixer[ui::kColorAshSystemUIMenuItemBackgroundSelected] = {kColorAshInkDrop};
   mixer[ui::kColorAshSystemUIMenuSeparator] = {kColorAshSeparatorColor};
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.cc b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
index 4d05636c..e94bf15 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_impl.cc
+++ b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/input_device_settings_controller.h"
 #include "ash/public/mojom/input_device_settings.mojom.h"
 #include "ash/session/session_controller_impl.h"
@@ -21,6 +22,7 @@
 #include "base/functional/bind.h"
 #include "base/notreached.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "ui/events/devices/input_device.h"
 
 namespace ash {
@@ -76,9 +78,7 @@
 }
 
 void InputDeviceSettingsControllerImpl::Init() {
-  if (features::IsInputDeviceSettingsSplitEnabled()) {
-    Shell::Get()->session_controller()->AddObserver(this);
-  }
+  Shell::Get()->session_controller()->AddObserver(this);
   keyboard_notifier_ =
       std::make_unique<InputDeviceNotifier<mojom::KeyboardPtr>>(
           &keyboards_,
@@ -104,9 +104,7 @@
 }
 
 InputDeviceSettingsControllerImpl::~InputDeviceSettingsControllerImpl() {
-  if (features::IsInputDeviceSettingsSplitEnabled()) {
-    Shell::Get()->session_controller()->RemoveObserver(this);
-  }
+  Shell::Get()->session_controller()->RemoveObserver(this);
 }
 
 void InputDeviceSettingsControllerImpl::RegisterProfilePrefs(
@@ -120,10 +118,55 @@
 
 void InputDeviceSettingsControllerImpl::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
+  // If the flag is disabled, clear all the settings dictionaries.
+  if (!features::IsInputDeviceSettingsSplitEnabled()) {
+    active_pref_service_ = nullptr;
+    pref_service->SetDict(prefs::kKeyboardDeviceSettingsDictPref, {});
+    pref_service->SetDict(prefs::kMouseDeviceSettingsDictPref, {});
+    pref_service->SetDict(prefs::kPointingStickDeviceSettingsDictPref, {});
+    pref_service->SetDict(prefs::kTouchpadDeviceSettingsDictPref, {});
+    return;
+  }
   active_pref_service_ = pref_service;
   // TODO(michaelcheco): Initialize settings and notify observers.
 }
 
+const mojom::KeyboardSettings*
+InputDeviceSettingsControllerImpl::GetKeyboardSettings(DeviceId id) {
+  auto iter = keyboards_.find(id);
+  if (iter == keyboards_.end()) {
+    return nullptr;
+  }
+  return iter->second->settings.get();
+}
+
+const mojom::MouseSettings* InputDeviceSettingsControllerImpl::GetMouseSettings(
+    DeviceId id) {
+  auto iter = mice_.find(id);
+  if (iter == mice_.end()) {
+    return nullptr;
+  }
+  return iter->second->settings.get();
+}
+
+const mojom::TouchpadSettings*
+InputDeviceSettingsControllerImpl::GetTouchpadSettings(DeviceId id) {
+  auto iter = touchpads_.find(id);
+  if (iter == touchpads_.end()) {
+    return nullptr;
+  }
+  return iter->second->settings.get();
+}
+
+const mojom::PointingStickSettings*
+InputDeviceSettingsControllerImpl::GetPointingStickSettings(DeviceId id) {
+  auto iter = pointing_sticks_.find(id);
+  if (iter == pointing_sticks_.end()) {
+    return nullptr;
+  }
+  return iter->second->settings.get();
+}
+
 std::vector<mojom::KeyboardPtr>
 InputDeviceSettingsControllerImpl::GetConnectedKeyboards() {
   std::vector<mojom::KeyboardPtr> keyboard_vector;
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.h b/ash/system/input_device_settings/input_device_settings_controller_impl.h
index ef616a7..21cbbad 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_impl.h
+++ b/ash/system/input_device_settings/input_device_settings_controller_impl.h
@@ -43,6 +43,11 @@
   std::vector<mojom::TouchpadPtr> GetConnectedTouchpads() override;
   std::vector<mojom::MousePtr> GetConnectedMice() override;
   std::vector<mojom::PointingStickPtr> GetConnectedPointingSticks() override;
+  const mojom::KeyboardSettings* GetKeyboardSettings(DeviceId id) override;
+  const mojom::MouseSettings* GetMouseSettings(DeviceId id) override;
+  const mojom::TouchpadSettings* GetTouchpadSettings(DeviceId id) override;
+  const mojom::PointingStickSettings* GetPointingStickSettings(
+      DeviceId id) override;
   void SetKeyboardSettings(DeviceId id,
                            const mojom::KeyboardSettings& settings) override;
   void AddObserver(Observer* observer) override;
diff --git a/ash/system/input_device_settings/input_device_settings_controller_unittest.cc b/ash/system/input_device_settings/input_device_settings_controller_unittest.cc
index 7a205966..18b1dc8fe 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_unittest.cc
+++ b/ash/system/input_device_settings/input_device_settings_controller_unittest.cc
@@ -7,15 +7,19 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/public/cpp/ash_prefs.h"
 #include "ash/public/cpp/input_device_settings_controller.h"
 #include "ash/public/mojom/input_device_settings.mojom.h"
+#include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/system/input_device_settings/input_device_settings_pref_names.h"
 #include "ash/system/input_device_settings/pref_handlers/keyboard_pref_handler.h"
 #include "ash/test/ash_test_base.h"
 #include "base/ranges/algorithm.h"
 #include "base/ranges/functional.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
 #include "ui/events/devices/input_device.h"
 
 namespace ash {
@@ -29,6 +33,8 @@
     10, ui::INPUT_DEVICE_BLUETOOTH, "kSampleKeyboardBluetooth"};
 const ui::InputDevice kSampleKeyboardUsb = {15, ui::INPUT_DEVICE_USB,
                                             "kSampleKeyboardUsb"};
+
+constexpr char kUserEmail[] = "example1@abc.com";
 }  // namespace
 
 class FakeKeyboardPrefHandler : public KeyboardPrefHandler {
@@ -166,4 +172,42 @@
   EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 2u);
 }
 
+TEST_F(InputDeviceSettingsControllerTest, DeletesPrefsWhenFlagDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
+
+  const AccountId account_id = AccountId::FromUserEmail(kUserEmail);
+
+  std::unique_ptr<TestingPrefServiceSimple> pref_service =
+      std::make_unique<TestingPrefServiceSimple>();
+  ash::RegisterUserProfilePrefs(pref_service->registry(), /*for_test=*/true);
+
+  base::Value::Dict test_pref_value;
+  test_pref_value.Set("Fake Key", base::Value::Dict());
+  pref_service->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
+                        test_pref_value.Clone());
+  pref_service->SetDict(prefs::kMouseDeviceSettingsDictPref,
+                        test_pref_value.Clone());
+  pref_service->SetDict(prefs::kPointingStickDeviceSettingsDictPref,
+                        test_pref_value.Clone());
+  pref_service->SetDict(prefs::kTouchpadDeviceSettingsDictPref,
+                        test_pref_value.Clone());
+
+  GetSessionControllerClient()->SetUserPrefService(account_id,
+                                                   std::move(pref_service));
+  SimulateUserLogin(account_id);
+
+  PrefService* active_pref_service =
+      Shell::Get()->session_controller()->GetActivePrefService();
+  EXPECT_EQ(base::Value::Dict(), active_pref_service->GetDict(
+                                     prefs::kKeyboardDeviceSettingsDictPref));
+  EXPECT_EQ(base::Value::Dict(),
+            active_pref_service->GetDict(prefs::kMouseDeviceSettingsDictPref));
+  EXPECT_EQ(base::Value::Dict(),
+            active_pref_service->GetDict(
+                prefs::kPointingStickDeviceSettingsDictPref));
+  EXPECT_EQ(base::Value::Dict(), active_pref_service->GetDict(
+                                     prefs::kTouchpadDeviceSettingsDictPref));
+}
+
 }  // namespace ash
diff --git a/ash/webui/projector_app/BUILD.gn b/ash/webui/projector_app/BUILD.gn
index ce2dc7bb..7b2aa84 100644
--- a/ash/webui/projector_app/BUILD.gn
+++ b/ash/webui/projector_app/BUILD.gn
@@ -10,8 +10,8 @@
 
 static_library("projector_app") {
   sources = [
-    "annotator_message_handler.cc",
-    "annotator_message_handler.h",
+    "annotator_page_handler_impl.cc",
+    "annotator_page_handler_impl.h",
     "projector_app_client.cc",
     "projector_app_client.h",
     "projector_message_handler.cc",
@@ -36,6 +36,8 @@
     "//ash/public/cpp",
     "//ash/strings",
     "//ash/webui/media_app_ui:buildflags",
+    "//ash/webui/projector_app/mojom:annotator_mojo_bindings",
+    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings",
     "//ash/webui/resources:media_app_bundle_resources",
     "//ash/webui/resources:projector_annotator_trusted_resources",
     "//ash/webui/resources:projector_annotator_untrusted_resources",
@@ -47,6 +49,7 @@
     "//components/prefs",
     "//components/signin/public/identity_manager",
     "//content/public/browser",
+    "//mojo/public/cpp/bindings",
     "//third_party/re2:re2",
     "//ui/resources",
     "//ui/webui",
@@ -79,7 +82,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "test/annotator_message_handler_unittest.cc",
+    "test/annotator_page_handler_impl_unittest.cc",
     "test/projector_message_handler_unittest.cc",
     "test/projector_oauth_token_fetcher_unittest.cc",
     "test/projector_xhr_sender_unittest.cc",
@@ -87,9 +90,12 @@
 
   deps = [
     ":test_support",
+    "//ash/webui/projector_app/mojom:annotator_mojo_bindings",
+    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings",
     "//base",
     "//components/prefs:test_support",
     "//content/test:test_support",
+    "//mojo/public/cpp/bindings",
     "//testing/gtest",
   ]
 }
diff --git a/ash/webui/projector_app/annotator_message_handler.cc b/ash/webui/projector_app/annotator_message_handler.cc
deleted file mode 100644
index 5375fae..0000000
--- a/ash/webui/projector_app/annotator_message_handler.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/webui/projector_app/annotator_message_handler.h"
-
-#include <memory>
-
-#include "ash/public/cpp/projector/annotator_tool.h"
-#include "ash/public/cpp/projector/projector_controller.h"
-#include "ash/webui/projector_app/projector_app_client.h"
-#include "base/check.h"
-#include "base/json/values_util.h"
-#include "base/values.h"
-#include "content/public/browser/web_ui.h"
-
-namespace ash {
-
-AnnotatorMessageHandler::AnnotatorMessageHandler() {
-  ProjectorAppClient::Get()->SetAnnotatorMessageHandler(this);
-}
-
-AnnotatorMessageHandler::~AnnotatorMessageHandler() {
-  ProjectorAppClient::Get()->ResetAnnotatorMessageHandler(this);
-}
-
-void AnnotatorMessageHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      "onUndoRedoAvailabilityChanged",
-      base::BindRepeating(
-          &AnnotatorMessageHandler::OnUndoRedoAvailabilityChanged,
-          base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
-      "onCanvasInitialized",
-      base::BindRepeating(&AnnotatorMessageHandler::OnCanvasInitialized,
-                          base::Unretained(this)));
-}
-
-void AnnotatorMessageHandler::SetTool(const AnnotatorTool& tool) {
-  AllowJavascript();
-  FireWebUIListener("setTool", tool.ToValue());
-}
-
-void AnnotatorMessageHandler::Undo() {
-  AllowJavascript();
-  FireWebUIListener("undo");
-}
-
-void AnnotatorMessageHandler::Redo() {
-  AllowJavascript();
-  FireWebUIListener("redo");
-}
-
-void AnnotatorMessageHandler::Clear() {
-  AllowJavascript();
-  FireWebUIListener("clear");
-}
-
-void AnnotatorMessageHandler::OnUndoRedoAvailabilityChanged(
-    const base::Value::List& args) {
-  DCHECK_EQ(args.size(), 2u);
-  DCHECK(args[0].is_bool());
-  DCHECK(args[1].is_bool());
-  ProjectorController::Get()->OnUndoRedoAvailabilityChanged(args[0].GetBool(),
-                                                            args[1].GetBool());
-}
-
-void AnnotatorMessageHandler::OnCanvasInitialized(
-    const base::Value::List& args) {
-  DCHECK_EQ(args.size(), 1u);
-  DCHECK(args[0].is_bool());
-  ProjectorController::Get()->OnCanvasInitialized(args[0].GetBool());
-}
-
-}  // namespace ash
diff --git a/ash/webui/projector_app/annotator_message_handler.h b/ash/webui/projector_app/annotator_message_handler.h
deleted file mode 100644
index 8b382f9f..0000000
--- a/ash/webui/projector_app/annotator_message_handler.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_MESSAGE_HANDLER_H_
-#define ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_MESSAGE_HANDLER_H_
-
-#include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "base/functional/callback.h"
-#include "base/values.h"
-#include "content/public/browser/web_ui_message_handler.h"
-
-namespace content {
-class WebUI;
-}  // namespace content
-
-namespace ash {
-
-struct AnnotatorTool;
-
-// Handles communication with the Annotator WebUI (i.e.
-// chrome://projector/annotator/annotator_embedder.html)
-class AnnotatorMessageHandler : public content::WebUIMessageHandler {
- public:
-  AnnotatorMessageHandler();
-  AnnotatorMessageHandler(const AnnotatorMessageHandler&) = delete;
-  AnnotatorMessageHandler& operator=(const AnnotatorMessageHandler&) = delete;
-  ~AnnotatorMessageHandler() override;
-
-  // content::WebUIMessageHandler:
-  void RegisterMessages() override;
-
-  void SetTool(const AnnotatorTool& tool);
-  void Undo();
-  void Redo();
-  void Clear();
-  void set_web_ui_for_test(content::WebUI* web_ui) { set_web_ui(web_ui); }
-  content::WebUI* get_web_ui_for_test() { return web_ui(); }
-
- private:
-  void OnToolSet(const base::Value::List& args);
-  void OnUndoRedoAvailabilityChanged(const base::Value::List& args);
-  void OnCanvasInitialized(const base::Value::List& args);
-  void OnError(const base::Value::List& args);
-};
-
-}  // namespace ash
-
-#endif  // ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_MESSAGE_HANDLER_H_
diff --git a/ash/webui/projector_app/annotator_page_handler_impl.cc b/ash/webui/projector_app/annotator_page_handler_impl.cc
new file mode 100644
index 0000000..4beb9ec3
--- /dev/null
+++ b/ash/webui/projector_app/annotator_page_handler_impl.cc
@@ -0,0 +1,82 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/webui/projector_app/annotator_page_handler_impl.h"
+
+#include <memory>
+
+#include "ash/public/cpp/projector/annotator_tool.h"
+#include "ash/public/cpp/projector/projector_controller.h"
+#include "ash/webui/projector_app/mojom/annotator.mojom.h"
+#include "ash/webui/projector_app/projector_app_client.h"
+#include "ash/webui/projector_app/public/mojom/annotator_structs.mojom.h"
+#include "base/check.h"
+#include "base/json/values_util.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+namespace ash {
+
+AnnotatorPageHandlerImpl::AnnotatorPageHandlerImpl(
+    mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
+        annotator_handler,
+    mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator,
+    content::WebUI* web_ui)
+    : annotator_remote_(std::move(annotator)),
+      annotator_handler_receiver_(this, std::move(annotator_handler)),
+      web_ui_(web_ui) {
+  ProjectorAppClient::Get()->SetAnnotatorPageHandler(this);
+}
+
+AnnotatorPageHandlerImpl::~AnnotatorPageHandlerImpl() {
+  ProjectorAppClient::Get()->ResetAnnotatorPageHandler(this);
+}
+
+void AnnotatorPageHandlerImpl::SetTool(const AnnotatorTool& tool) {
+  auto mojo_tool = annotator::mojom::AnnotatorTool::New();
+  mojo_tool->color = tool.GetColorHexString();
+  mojo_tool->tool = tool.GetToolString();
+  mojo_tool->size = tool.size;
+  annotator_remote_->SetTool(std::move(mojo_tool));
+}
+
+void AnnotatorPageHandlerImpl::Undo() {
+  annotator_remote_->Undo();
+}
+
+void AnnotatorPageHandlerImpl::Redo() {
+  annotator_remote_->Redo();
+}
+
+void AnnotatorPageHandlerImpl::Clear() {
+  annotator_remote_->Clear();
+}
+
+void AnnotatorPageHandlerImpl::OnUndoRedoAvailabilityChanged(
+    bool undo_available,
+    bool redo_available) {
+  // ProjectorController is created when ash::Shell::Init is called and is
+  // destroyed when ash::Shell is destroyed. Therefore, ProjectorController
+  // is available when this WebUI is showing.
+  ProjectorController::Get()->OnUndoRedoAvailabilityChanged(undo_available,
+                                                            redo_available);
+}
+
+void AnnotatorPageHandlerImpl::OnCanvasInitialized(bool success) {
+  // ProjectorController is created when ash::Shell::Init is called and is
+  // destroyed when ash::Shell is destroyed. Therefore, ProjectorController
+  // is available when this WebUI is showing.
+  ProjectorController::Get()->OnCanvasInitialized(success);
+}
+
+void AnnotatorPageHandlerImpl::OnError(
+    const std::vector<std::string>& messages) {
+  for (const auto& message : messages) {
+    LOG(ERROR) << message;
+  }
+
+  // TODO(b/239979179): Consider reloading the webcontent.
+}
+
+}  // namespace ash
diff --git a/ash/webui/projector_app/annotator_page_handler_impl.h b/ash/webui/projector_app/annotator_page_handler_impl.h
new file mode 100644
index 0000000..8808cef0
--- /dev/null
+++ b/ash/webui/projector_app/annotator_page_handler_impl.h
@@ -0,0 +1,64 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_PAGE_HANDLER_IMPL_H_
+#define ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_PAGE_HANDLER_IMPL_H_
+
+#include "ash/public/cpp/projector/projector_annotator_controller.h"
+#include "ash/webui/projector_app/mojom/annotator.mojom.h"
+#include "base/functional/callback.h"
+#include "base/values.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace content {
+class WebUI;
+}  // namespace content
+
+namespace ash {
+
+struct AnnotatorTool;
+
+// Handles communication with the Annotator WebUI (i.e.
+// chrome://projector/annotator/annotator_embedder.html).
+class AnnotatorPageHandlerImpl : public annotator::mojom::AnnotatorPageHandler {
+ public:
+  AnnotatorPageHandlerImpl(
+      mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
+          annotator_handler,
+      mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator,
+      content::WebUI* web_ui);
+  AnnotatorPageHandlerImpl(const AnnotatorPageHandlerImpl&) = delete;
+  AnnotatorPageHandlerImpl& operator=(const AnnotatorPageHandlerImpl&) = delete;
+  ~AnnotatorPageHandlerImpl() override;
+
+  // Called by ProjectorAppClient.
+  void SetTool(const AnnotatorTool& tool);
+  void Undo();
+  void Redo();
+  void Clear();
+
+  // annotator::mojom::AnnotatorHandler:
+  void OnUndoRedoAvailabilityChanged(bool undo_available,
+                                     bool redo_available) override;
+  void OnCanvasInitialized(bool success) override;
+  void OnError(const std::vector<std::string>& messages) override;
+
+  content::WebUI* get_web_ui_for_test() { return web_ui_; }
+
+ private:
+  mojo::Remote<annotator::mojom::AnnotatorPage> annotator_remote_;
+  mojo::Receiver<annotator::mojom::AnnotatorPageHandler>
+      annotator_handler_receiver_;
+
+  // The WebUI that owns the TrustedProjectorAnnotatorUI that owns this
+  // instance.
+  content::WebUI* const web_ui_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_PAGE_HANDLER_IMPL_H_
diff --git a/ash/webui/projector_app/mojom/BUILD.gn b/ash/webui/projector_app/mojom/BUILD.gn
new file mode 100644
index 0000000..95890cd
--- /dev/null
+++ b/ash/webui/projector_app/mojom/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/common/features.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(is_chromeos_ash, "Projector Annotator is ChromeOS only")
+
+cur_dir = rebase_path(".", "//")
+
+mojom("annotator_mojo_bindings") {
+  sources = [ "annotator.mojom" ]
+  deps = [ "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings" ]
+  webui_module_path = "/$cur_dir"
+}
diff --git a/ash/webui/projector_app/mojom/OWNERS b/ash/webui/projector_app/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/ash/webui/projector_app/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ash/webui/projector_app/mojom/annotator.mojom b/ash/webui/projector_app/mojom/annotator.mojom
new file mode 100644
index 0000000..fca08dc
--- /dev/null
+++ b/ash/webui/projector_app/mojom/annotator.mojom
@@ -0,0 +1,46 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.annotator.mojom;
+
+import "ash/webui/projector_app/public/mojom/annotator_structs.mojom";
+
+// AnnotatorPage interface implemented in the Javascript. Used by the browser
+// process to send events to Javascript.
+interface AnnotatorPage {
+    // Clears the annotations on the annotator canvas.
+    Clear();
+    // Undo the last stroke on the annotator canvas.
+    Undo();
+    // Redo the last stroke on the annotator canvas.
+    Redo();
+    // Sets the tool the user uses to annotate.
+    SetTool(AnnotatorTool tool);
+};
+
+// AnnotatoPagerHandler interface implemented in C++ in the browser process.
+// Used by Javascript to communicate with the browser process.
+interface AnnotatorPageHandler {
+   // Notifies the browser that that undo/redo availability
+   // changed for annotator.
+   OnUndoRedoAvailabilityChanged(bool undo_available,
+                                 bool redo_available);
+
+  // Notifies the browser process that the annotation canvas
+  // has been initialized.
+  OnCanvasInitialized(bool success);
+
+  // Notifies the browser process that an error has occurred
+  // in the renderer and sends the error messages to it.
+  OnError(array<string> errors);
+};
+
+// Interface used to setup the communication between Javascript and C++
+// in the browser process.
+interface AnnotatorPageHandlerFactory {
+  // Creates the AnnotatorPageHandler in the browser process and binds it
+  // to receive calls from Javascript.
+  Create(pending_receiver<AnnotatorPageHandler> handler,
+         pending_remote<AnnotatorPage> annotator);
+};
diff --git a/ash/webui/projector_app/projector_app_client.h b/ash/webui/projector_app/projector_app_client.h
index e73ee6f..5c46efe 100644
--- a/ash/webui/projector_app/projector_app_client.h
+++ b/ash/webui/projector_app/projector_app_client.h
@@ -28,7 +28,7 @@
 
 namespace ash {
 
-class AnnotatorMessageHandler;
+class AnnotatorPageHandlerImpl;
 struct AnnotatorTool;
 struct ProjectorScreencastVideo;
 struct NewScreencastPrecondition;
@@ -161,14 +161,13 @@
                         const std::string& resource_key,
                         OnGetVideoCallback callback) const = 0;
 
-  // Registers the AnnotatorMessageHandler that is owned by the WebUI that
+  // Registers the AnnotatorPageHandlerImpl that is owned by the WebUI that
   // contains the Projector annotator.
-  virtual void SetAnnotatorMessageHandler(AnnotatorMessageHandler* handler) = 0;
+  virtual void SetAnnotatorPageHandler(AnnotatorPageHandlerImpl* handler) = 0;
 
-  // Resets the stored AnnotatorMessageHandler if it matches the one that is
+  // Resets the stored AnnotatorPageHandlerImpl if it matches the one that is
   // passed in.
-  virtual void ResetAnnotatorMessageHandler(
-      AnnotatorMessageHandler* handler) = 0;
+  virtual void ResetAnnotatorPageHandler(AnnotatorPageHandlerImpl* handler) = 0;
 
   // Sets the tool inside the annotator WebUI.
   virtual void SetTool(const AnnotatorTool& tool) = 0;
diff --git a/ash/webui/projector_app/public/mojom/BUILD.gn b/ash/webui/projector_app/public/mojom/BUILD.gn
new file mode 100644
index 0000000..c6da2f9a
--- /dev/null
+++ b/ash/webui/projector_app/public/mojom/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/common/features.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(is_chromeos_ash, "Projector Annotator is ChromeOS only")
+
+cur_dir = rebase_path(".", "//")
+
+mojom("annotator_mojo_bindings") {
+  sources = [ "annotator_structs.mojom" ]
+  webui_module_path = "/$cur_dir"
+}
diff --git a/ash/webui/projector_app/public/mojom/OWNERS b/ash/webui/projector_app/public/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/ash/webui/projector_app/public/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ash/webui/projector_app/public/mojom/annotator_structs.mojom b/ash/webui/projector_app/public/mojom/annotator_structs.mojom
new file mode 100644
index 0000000..eda84fd
--- /dev/null
+++ b/ash/webui/projector_app/public/mojom/annotator_structs.mojom
@@ -0,0 +1,16 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.annotator.mojom;
+
+// The annotation tool struct sent from the browser process to
+// javascript.
+struct AnnotatorTool {
+   // TODO(b/239979179): Consider adding an enum
+   // to represent the tool instead of just accepting a
+   // string.
+   string tool;
+   string color;
+   int32 size;
+};
diff --git a/ash/webui/projector_app/resources/BUILD.gn b/ash/webui/projector_app/resources/BUILD.gn
index 840e423..0cae46dc 100644
--- a/ash/webui/projector_app/resources/BUILD.gn
+++ b/ash/webui/projector_app/resources/BUILD.gn
@@ -19,7 +19,11 @@
     "//ash/webui/projector_app/resources/mock:mock_app",
   ]
 
-  closure_flags = default_closure_args + [ "browser_resolver_prefix_replacements=\"chrome-untrusted://projector/common/=" + rebase_path(
-                                               "//ash/webui/projector_app/resources/common/",
-                                               root_build_dir) + "\"" ]
+  closure_flags = default_closure_args + [
+                    "browser_resolver_prefix_replacements=\"chrome-untrusted://projector/common/=" + rebase_path(
+                            "//ash/webui/projector_app/resources/common/",
+                            root_build_dir) + "\"",
+                    "hide_warnings_for=ash/webui/projector_app/public/mojom/",
+                    "hide_warnings_for=ash/webui/projector_app/mojom/",
+                  ]
 }
diff --git a/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn b/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
index 490ceed..88d8fbd 100644
--- a/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
+++ b/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
@@ -15,18 +15,14 @@
     "trusted_annotator_comm_factory.js",
   ]
   deps = [
-    "//ash/webui/common/resources:cr.m",
     "//ash/webui/common/resources:cr_deprecated",
-    "//ash/webui/common/resources:web_ui_listener_behavior",
     "//ash/webui/common/resources/post_message_api:post_message_api_client",
     "//ash/webui/common/resources/post_message_api:post_message_api_request_handler",
+    "//ash/webui/projector_app/mojom:annotator_mojo_bindings_webui_js",
+    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings_webui_js",
     "//ash/webui/projector_app/resources/common:message_types",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
-  externs_list = [
-    "$externs_path/chrome_send.js",
-    "../../common/projector_app.externs.js",
-  ]
+  externs_list = [ "../../common/projector_app.externs.js" ]
 }
 
 generate_grd("build_trusted_grd") {
@@ -38,7 +34,16 @@
     "annotator_embedder_impl.js",
   ]
 
+  manifest_files = []
   input_files_base_dir = rebase_path(".", "//")
   grd_prefix = "ash_projector_annotator_trusted"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+  deps = [
+    "//ash/webui/projector_app/mojom:annotator_mojo_bindings_webui_grdp",
+    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings_webui_grdp",
+  ]
+  grdp_files = [
+    "$target_gen_dir/../../../mojom/annotator_mojo_bindings_webui_resources.grdp",
+    "$target_gen_dir/../../../public/mojom/annotator_mojo_bindings_webui_resources.grdp",
+  ]
 }
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js b/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
index 2b4a04e..322941c5 100644
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
+++ b/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
@@ -1,10 +1,10 @@
 // Copyright 2022 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-
-import {sendWithPromise} from 'chrome://resources/ash/common/cr.m.js';
 import {addSingletonGetter} from 'chrome://resources/ash/common/cr_deprecated.js';
 
+import {AnnotatorPageCallbackRouter, AnnotatorPageHandlerFactory, AnnotatorPageHandlerRemote, AnnotatorPageRemote} from './ash/webui/projector_app/mojom/annotator.mojom-webui.js';
+
 /**
  * To use the annotator proxy, please import this module and call
  * AnnotatorBrowserProxyImpl.getInstance().*
@@ -42,21 +42,35 @@
  * @implements {AnnotatorBrowserProxy}
  */
 export class AnnotatorBrowserProxyImpl {
+  constructor() {
+    this.pageHandlerFactory = AnnotatorPageHandlerFactory.getRemote();
+    this.pageHandlerRemote = new AnnotatorPageHandlerRemote();
+    this.annotatorCallbackRouter = new AnnotatorPageCallbackRouter();
+
+    this.pageHandlerFactory.create(
+        this.pageHandlerRemote.$.bindNewPipeAndPassReceiver(),
+        this.annotatorCallbackRouter.$.bindNewPipeAndPassRemote());
+  }
+
+  getAnnotatorCallbackRouter() {
+    return this.annotatorCallbackRouter;
+  }
+
   /** @override */
   onUndoRedoAvailabilityChanged(undoAvailable, redoAvailable) {
-    return chrome.send(
-        'onUndoRedoAvailabilityChanged', [undoAvailable, redoAvailable]);
+    this.pageHandlerRemote.onUndoRedoAvailabilityChanged(
+        undoAvailable, redoAvailable);
   }
 
   /** @override */
   onCanvasInitialized(success) {
-    return chrome.send('onCanvasInitialized', [success]);
+    this.pageHandlerRemote.onCanvasInitialized(success);
   }
 
   /** @override */
-  onError(msg) {
-    return chrome.send('onError', msg);
+  onError(msgs) {
+    this.pageHandlerRemote.onError(msgs);
   }
 }
 
-addSingletonGetter(AnnotatorBrowserProxyImpl);
+addSingletonGetter(AnnotatorBrowserProxyImpl);
\ No newline at end of file
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
index f3b414a..105c6d3 100644
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
+++ b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
@@ -8,9 +8,6 @@
 <html>
 <head>
     <link rel="stylesheet" href="annotator_embedder.css">
-    <script type="module" src="common/message_types.js"></script>
-    <script type="module" src="annotator_browser_proxy.js"></script>
-    <script type="module" src="trusted_annotator_comm_factory.js"></script>
     <script type="module" src="annotator_embedder_impl.js"></script>
   </head>
   <body>
@@ -19,6 +16,5 @@
       src="chrome-untrusted://projector-annotator"
       allow="cross-origin-isolated">
     </iframe>
-    <annotator-embedder-impl></annotator-embedder-impl>
   </body>
 </html>
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
index 3cc6d58..97652a40 100644
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
+++ b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
@@ -1,12 +1,10 @@
-// Copyright 2021 The Chromium Authors
+// Copyright 2021 The Chromium Authors.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {WebUIListenerBehavior} from 'chrome://resources/ash/common/web_ui_listener_behavior.js';
-import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
 import {AnnotatorBrowserProxyImpl} from './annotator_browser_proxy.js';
-import {AnnotatorTrustedCommFactory, UntrustedAnnotatorClient} from './trusted_annotator_comm_factory.js';
+import {AnnotatorPageCallbackRouter} from './ash/webui/projector_app/mojom/annotator.mojom-webui.js';
+import {AnnotatorTrustedCommFactory} from './trusted_annotator_comm_factory.js';
 
 /**
  * Enum for passing annotator error message to the browser process.
@@ -19,49 +17,48 @@
   SET_TOOL_ERROR: 'SET_TOOL_ERROR',
 };
 
-Polymer({
-  is: 'annotator-embedder-impl',
 
-  behaviors: [WebUIListenerBehavior],
+/* @type {UntrustedAnnotatorClient} */
+const client = AnnotatorTrustedCommFactory.getPostMessageAPIClient();
 
-  /** @override */
-  ready() {
-    const client = AnnotatorTrustedCommFactory.getPostMessageAPIClient();
+/* @type {AnnotatorPageCallbackRouter} */
+const annotatorPageRouter =
+    AnnotatorBrowserProxyImpl.getInstance().getAnnotatorCallbackRouter();
 
-    this.addWebUIListener('undo', () => {
-      try {
-        client.undo();
-      } catch (error) {
-        AnnotatorBrowserProxyImpl.getInstance().onError(
-            [AnnotatorToolErrorType.UNDO_ERROR]);
-      }
-    });
-
-    this.addWebUIListener('redo', () => {
-      try {
-        client.redo();
-      } catch (error) {
-        AnnotatorBrowserProxyImpl.getInstance().onError(
-            [AnnotatorToolErrorType.REDO_ERROR]);
-      }
-    });
-
-    this.addWebUIListener('clear', () => {
-      try {
-        client.clear();
-      } catch (error) {
-        AnnotatorBrowserProxyImpl.getInstance().onError(
-            [AnnotatorToolErrorType.CLEAR_ERROR]);
-      }
-    });
-
-    this.addWebUIListener('setTool', async (tool) => {
-      try {
-        client.setTool(tool);
-      } catch (error) {
-        AnnotatorBrowserProxyImpl.getInstance().onError(
-            [AnnotatorToolErrorType.SET_TOOL_ERROR]);
-      }
-    });
-  },
+annotatorPageRouter.undo.addListener(() => {
+  try {
+    client.undo();
+  } catch (error) {
+    AnnotatorBrowserProxyImpl.getInstance().onError(
+        [AnnotatorToolErrorType.UNDO_ERROR]);
+  }
 });
+
+annotatorPageRouter.redo.addListener(() => {
+  try {
+    client.redo();
+  } catch (error) {
+    AnnotatorBrowserProxyImpl.getInstance().onError(
+        [AnnotatorToolErrorType.REDO_ERROR]);
+  }
+});
+
+
+annotatorPageRouter.clear.addListener(() => {
+  try {
+    client.clear();
+  } catch (error) {
+    AnnotatorBrowserProxyImpl.getInstance().onError(
+        [AnnotatorToolErrorType.CLEAR_ERROR]);
+  }
+});
+
+
+annotatorPageRouter.setTool.addListener((tool) => {
+  try {
+    client.setTool(tool);
+  } catch (error) {
+    AnnotatorBrowserProxyImpl.getInstance().onError(
+        [AnnotatorToolErrorType.SET_TOOL_ERROR]);
+  }
+});
\ No newline at end of file
diff --git a/ash/webui/projector_app/resources/app/trusted/BUILD.gn b/ash/webui/projector_app/resources/app/trusted/BUILD.gn
index 389eadd9..bb8b80c 100644
--- a/ash/webui/projector_app/resources/app/trusted/BUILD.gn
+++ b/ash/webui/projector_app/resources/app/trusted/BUILD.gn
@@ -22,6 +22,7 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
   externs_list = [
+    "$externs_path/chrome_send.js",
     "../../common/projector_app.externs.js",
     "//ash/webui/web_applications/externs/file_handling.externs.js",
   ]
diff --git a/ash/webui/projector_app/test/annotator_message_handler_unittest.cc b/ash/webui/projector_app/test/annotator_message_handler_unittest.cc
deleted file mode 100644
index 20df887..0000000
--- a/ash/webui/projector_app/test/annotator_message_handler_unittest.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/webui/projector_app/annotator_message_handler.h"
-
-#include "ash/public/cpp/projector/annotator_tool.h"
-#include "ash/public/cpp/test/mock_projector_controller.h"
-#include "ash/webui/projector_app/test/mock_app_client.h"
-#include "base/test/task_environment.h"
-#include "base/values.h"
-#include "content/public/test/test_web_ui.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace ash {
-
-namespace {
-
-const char kWebUIListenerCall[] = "cr.webUIListenerCallback";
-
-}  // namespace
-
-class AnnotatorMessageHandlerTest : public testing::Test {
- public:
-  AnnotatorMessageHandlerTest() = default;
-  AnnotatorMessageHandlerTest(const AnnotatorMessageHandlerTest&) = delete;
-  AnnotatorMessageHandler& operator=(const AnnotatorMessageHandlerTest&) =
-      delete;
-  ~AnnotatorMessageHandlerTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    message_handler_ = std::make_unique<AnnotatorMessageHandler>();
-    message_handler_->set_web_ui_for_test(&web_ui());
-    message_handler_->RegisterMessages();
-  }
-
-  void TearDown() override { message_handler_.reset(); }
-
-  void ExpectCallToWebUI(const std::string& type,
-                         const std::string& func_name,
-                         size_t count) {
-    EXPECT_EQ(web_ui().call_data().size(), count);
-    const content::TestWebUI::CallData& call_data = *(web_ui().call_data()[0]);
-    EXPECT_EQ(call_data.function_name(), type);
-    EXPECT_EQ(call_data.arg1()->GetString(), func_name);
-  }
-
-  void SendUndoRedoAvailableChanged(bool undo_available, bool redo_available) {
-    base::Value::List list_args;
-    list_args.Append(base::Value(undo_available));
-    list_args.Append(base::Value(redo_available));
-    web_ui().HandleReceivedMessage("onUndoRedoAvailabilityChanged", list_args);
-  }
-
-  void SendCanvasInitialized(bool success) {
-    base::Value::List list_args;
-    list_args.Append(base::Value(success));
-    web_ui().HandleReceivedMessage("onCanvasInitialized", list_args);
-  }
-
-  content::TestWebUI& web_ui() { return web_ui_; }
-  AnnotatorMessageHandler* handler() { return message_handler_.get(); }
-  MockProjectorController& controller() { return controller_; }
-
- private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-
-  std::unique_ptr<AnnotatorMessageHandler> message_handler_;
-  content::TestWebUI web_ui_;
-  MockProjectorController controller_;
-  MockAppClient client_;
-};
-
-TEST_F(AnnotatorMessageHandlerTest, SetTool) {
-  AnnotatorTool expected_tool;
-  expected_tool.color = SkColorSetARGB(0xA1, 0xB2, 0xC3, 0xD4);
-  expected_tool.size = 5;
-  expected_tool.type = AnnotatorToolType::kPen;
-  handler()->SetTool(expected_tool);
-
-  // Let's check that the call has been made.
-  ExpectCallToWebUI(kWebUIListenerCall, "setTool", /* call_count = */ 1u);
-  const content::TestWebUI::CallData& call_data = *(web_ui().call_data()[0]);
-
-  AnnotatorTool requested_tool = AnnotatorTool::FromValue(*call_data.arg2());
-  EXPECT_EQ(requested_tool, expected_tool);
-}
-
-TEST_F(AnnotatorMessageHandlerTest, Undo) {
-  handler()->Undo();
-  ExpectCallToWebUI(kWebUIListenerCall, "undo", /* call_count = */ 1u);
-}
-
-TEST_F(AnnotatorMessageHandlerTest, Redo) {
-  handler()->Redo();
-  ExpectCallToWebUI(kWebUIListenerCall, "redo", /* call_count = */ 1u);
-}
-
-TEST_F(AnnotatorMessageHandlerTest, Clear) {
-  handler()->Clear();
-  ExpectCallToWebUI(kWebUIListenerCall, "clear", /* call_count = */ 1u);
-}
-
-TEST_F(AnnotatorMessageHandlerTest, UndoRedoAvailabilityChanged) {
-  EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(false, false));
-  SendUndoRedoAvailableChanged(false, false);
-
-  EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(true, true));
-  SendUndoRedoAvailableChanged(true, true);
-
-  EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(false, true));
-  SendUndoRedoAvailableChanged(false, true);
-}
-
-TEST_F(AnnotatorMessageHandlerTest, CanvasInitialized) {
-  EXPECT_CALL(controller(), OnCanvasInitialized(true));
-  SendCanvasInitialized(true);
-
-  EXPECT_CALL(controller(), OnCanvasInitialized(false));
-  SendCanvasInitialized(false);
-}
-
-}  // namespace ash
diff --git a/ash/webui/projector_app/test/annotator_page_handler_impl_unittest.cc b/ash/webui/projector_app/test/annotator_page_handler_impl_unittest.cc
new file mode 100644
index 0000000..c0ecb750
--- /dev/null
+++ b/ash/webui/projector_app/test/annotator_page_handler_impl_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/webui/projector_app/annotator_page_handler_impl.h"
+
+#include "ash/public/cpp/projector/annotator_tool.h"
+#include "ash/public/cpp/test/mock_projector_controller.h"
+#include "ash/webui/projector_app/mojom/annotator.mojom.h"
+#include "ash/webui/projector_app/public/mojom/annotator_structs.mojom.h"
+#include "ash/webui/projector_app/test/mock_app_client.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "content/public/test/test_web_ui.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace ash {
+
+namespace {
+
+// MOCK the annotator instance in the WebUI renderer.
+class MockAnnotatorPage : public annotator::mojom::AnnotatorPage {
+ public:
+  MockAnnotatorPage() = default;
+  MockAnnotatorPage(const MockAnnotatorPage&) = delete;
+  MockAnnotatorPage& operator=(const MockAnnotatorPage&) = delete;
+  ~MockAnnotatorPage() override = default;
+
+  MOCK_METHOD0(Clear, void());
+  MOCK_METHOD0(Undo, void());
+  MOCK_METHOD0(Redo, void());
+  MOCK_METHOD1(SetTool, void(annotator::mojom::AnnotatorToolPtr tool));
+
+  void FlushReceiverForTesting() { receiver_.FlushForTesting(); }
+
+  void FlushRemoteForTesting() { remote_.FlushForTesting(); }
+
+  void SendUndoRedoAvailableChanged(bool undo_available, bool redo_available) {
+    remote_->OnUndoRedoAvailabilityChanged(undo_available, redo_available);
+  }
+
+  void SendCanvasInitialized(bool success) {
+    remote_->OnCanvasInitialized(success);
+  }
+
+  mojo::Receiver<annotator::mojom::AnnotatorPage>& receiver() {
+    return receiver_;
+  }
+  mojo::Remote<annotator::mojom::AnnotatorPageHandler>& remote() {
+    return remote_;
+  }
+
+ private:
+  mojo::Receiver<annotator::mojom::AnnotatorPage> receiver_{this};
+  mojo::Remote<annotator::mojom::AnnotatorPageHandler> remote_;
+};
+
+}  // namespace
+
+class AnnotatorPageHandlerImplTest : public testing::Test {
+ public:
+  AnnotatorPageHandlerImplTest() = default;
+  AnnotatorPageHandlerImplTest(const AnnotatorPageHandlerImplTest&) = delete;
+  AnnotatorPageHandlerImplTest& operator=(const AnnotatorPageHandlerImplTest&) =
+      delete;
+  ~AnnotatorPageHandlerImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    annotator_ = std::make_unique<MockAnnotatorPage>();
+    handler_ = std::make_unique<AnnotatorPageHandlerImpl>(
+        annotator().remote().BindNewPipeAndPassReceiver(),
+        annotator().receiver().BindNewPipeAndPassRemote(),
+        /*web_ui=*/nullptr);
+  }
+
+  void TearDown() override {
+    annotator_.reset();
+    handler_.reset();
+  }
+
+  AnnotatorPageHandlerImpl& handler() { return *handler_; }
+  MockProjectorController& controller() { return controller_; }
+  MockAnnotatorPage& annotator() { return *annotator_; }
+  base::test::SingleThreadTaskEnvironment& task_environment() {
+    return task_environment_;
+  }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+
+  std::unique_ptr<MockAnnotatorPage> annotator_;
+  std::unique_ptr<AnnotatorPageHandlerImpl> handler_;
+  MockProjectorController controller_;
+  MockAppClient client_;
+};
+
+TEST_F(AnnotatorPageHandlerImplTest, SetTool) {
+  AnnotatorTool expected_tool;
+  expected_tool.color = SkColorSetARGB(0xA1, 0xB2, 0xC3, 0xD4);
+  expected_tool.size = 5;
+  expected_tool.type = AnnotatorToolType::kPen;
+  EXPECT_CALL(annotator(), SetTool)
+      .WillOnce(testing::Invoke([&](annotator::mojom::AnnotatorToolPtr tool) {
+        EXPECT_EQ(tool->size, expected_tool.size);
+        EXPECT_EQ(tool->tool, expected_tool.GetToolString());
+        EXPECT_EQ(tool->color, expected_tool.GetColorHexString());
+      }));
+
+  handler().SetTool(expected_tool);
+  annotator().FlushReceiverForTesting();
+}
+
+TEST_F(AnnotatorPageHandlerImplTest, Undo) {
+  EXPECT_CALL(annotator(), Undo());
+  handler().Undo();
+  annotator().FlushReceiverForTesting();
+}
+
+TEST_F(AnnotatorPageHandlerImplTest, Redo) {
+  EXPECT_CALL(annotator(), Redo());
+  handler().Redo();
+  annotator().FlushReceiverForTesting();
+}
+
+TEST_F(AnnotatorPageHandlerImplTest, Clear) {
+  EXPECT_CALL(annotator(), Clear());
+  handler().Clear();
+  annotator().FlushReceiverForTesting();
+}
+
+TEST_F(AnnotatorPageHandlerImplTest, UndoRedoAvailabilityChanged) {
+  EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(false, false));
+  annotator().SendUndoRedoAvailableChanged(false, false);
+
+  EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(true, true));
+  annotator().SendUndoRedoAvailableChanged(true, true);
+
+  EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(false, true));
+  annotator().SendUndoRedoAvailableChanged(false, true);
+  annotator().FlushRemoteForTesting();
+}
+
+TEST_F(AnnotatorPageHandlerImplTest, CanvasInitialized) {
+  EXPECT_CALL(controller(), OnCanvasInitialized(true));
+  annotator().SendCanvasInitialized(true);
+
+  EXPECT_CALL(controller(), OnCanvasInitialized(false));
+  annotator().SendCanvasInitialized(false);
+  annotator().FlushRemoteForTesting();
+}
+
+}  // namespace ash
diff --git a/ash/webui/projector_app/test/mock_app_client.h b/ash/webui/projector_app/test/mock_app_client.h
index ab25668a..1a6b57fb 100644
--- a/ash/webui/projector_app/test/mock_app_client.h
+++ b/ash/webui/projector_app/test/mock_app_client.h
@@ -55,8 +55,8 @@
                      void(const std::string&,
                           const std::string&,
                           ProjectorAppClient::OnGetVideoCallback));
-  MOCK_METHOD1(SetAnnotatorMessageHandler, void(AnnotatorMessageHandler*));
-  MOCK_METHOD1(ResetAnnotatorMessageHandler, void(AnnotatorMessageHandler*));
+  MOCK_METHOD1(SetAnnotatorPageHandler, void(AnnotatorPageHandlerImpl*));
+  MOCK_METHOD1(ResetAnnotatorPageHandler, void(AnnotatorPageHandlerImpl*));
   MOCK_METHOD1(SetTool, void(const AnnotatorTool&));
   MOCK_METHOD0(Clear, void());
   MOCK_METHOD1(NotifyAppUIActive, void(bool active));
diff --git a/ash/webui/projector_app/trusted_projector_annotator_ui.cc b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
index 9650a73..d8c2b3e9 100644
--- a/ash/webui/projector_app/trusted_projector_annotator_ui.cc
+++ b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
@@ -9,13 +9,14 @@
 #include "ash/webui/grit/ash_projector_annotator_trusted_resources_map.h"
 #include "ash/webui/grit/ash_projector_common_resources.h"
 #include "ash/webui/grit/ash_projector_common_resources_map.h"
-#include "ash/webui/projector_app/annotator_message_handler.h"
+#include "ash/webui/projector_app/annotator_page_handler_impl.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/url_constants.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "url/gurl.h"
 
@@ -59,14 +60,7 @@
     content::WebUI* web_ui,
     const GURL& url,
     PrefService* pref_service)
-    : MojoBubbleWebUIController(web_ui, /*enable_chrome_send=*/true) {
-  // Multiple WebUIs (and therefore TrustedProjectorAnnotatorUIs) are created
-  // for a single Projector recording session, so a new AnnotatorMessageHandler
-  // needs to be created each time and attached to the new WebUI. The new
-  // handler is then referenced in ProjectorClientImpl.
-  auto handler = std::make_unique<ash::AnnotatorMessageHandler>();
-  web_ui->AddMessageHandler(std::move(handler));
-
+    : MojoBubbleWebUIController(web_ui, /*enable_chrome_send=*/false) {
   CreateAndAddProjectorAnnotatorHTMLSource(web_ui);
 
   // The Annotator and Projector SWA embed contents in a sandboxed
@@ -76,6 +70,27 @@
 
 TrustedProjectorAnnotatorUI::~TrustedProjectorAnnotatorUI() = default;
 
+void TrustedProjectorAnnotatorUI::BindInterface(
+    mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandlerFactory>
+        receiver) {
+  if (receiver_.is_bound()) {
+    receiver_.reset();
+  }
+  receiver_.Bind(std::move(receiver));
+}
+
+void TrustedProjectorAnnotatorUI::Create(
+    mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
+        annotator_handler,
+    mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator) {
+  // Multiple WebUIs (and therefore TrustedProjectorAnnotatorUIs) are created
+  // for a single Projector recording session, so a new AnnotatorMessageHandler
+  // needs to be created each time and attached to the new WebUI. The new
+  // handler is then referenced in ProjectorClientImpl.
+  handler_ = std::make_unique<AnnotatorPageHandlerImpl>(
+      std::move(annotator_handler), std::move(annotator), web_ui());
+}
+
 WEB_UI_CONTROLLER_TYPE_IMPL(TrustedProjectorAnnotatorUI)
 
 }  // namespace ash
diff --git a/ash/webui/projector_app/trusted_projector_annotator_ui.h b/ash/webui/projector_app/trusted_projector_annotator_ui.h
index 9c427562..ea79b58 100644
--- a/ash/webui/projector_app/trusted_projector_annotator_ui.h
+++ b/ash/webui/projector_app/trusted_projector_annotator_ui.h
@@ -5,7 +5,11 @@
 #ifndef ASH_WEBUI_PROJECTOR_APP_TRUSTED_PROJECTOR_ANNOTATOR_UI_H_
 #define ASH_WEBUI_PROJECTOR_APP_TRUSTED_PROJECTOR_ANNOTATOR_UI_H_
 
+#include "ash/webui/projector_app/mojom/annotator.mojom.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/webui/mojo_bubble_web_ui_controller.h"
 
 class GURL;
@@ -13,9 +17,13 @@
 
 namespace ash {
 
+class AnnotatorPageHandlerImpl;
+
 // The implementation for the Projector annotator for screen recording
 // annotations.
-class TrustedProjectorAnnotatorUI : public ui::MojoBubbleWebUIController {
+class TrustedProjectorAnnotatorUI
+    : public ui::MojoBubbleWebUIController,
+      annotator::mojom::AnnotatorPageHandlerFactory {
  public:
   TrustedProjectorAnnotatorUI(content::WebUI* web_ui,
                               const GURL& url,
@@ -25,8 +33,23 @@
   TrustedProjectorAnnotatorUI& operator=(const TrustedProjectorAnnotatorUI&) =
       delete;
 
+  void BindInterface(
+      mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandlerFactory>
+          factory);
+
  private:
   WEB_UI_CONTROLLER_TYPE_DECL();
+
+  // annotator::mojom::AnnotatorPageHandlerFactory:
+  void Create(
+      mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
+          annotator_handler,
+      mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator) override;
+
+  mojo::Receiver<annotator::mojom::AnnotatorPageHandlerFactory> receiver_{this};
+
+  // Handler for requests coming from the web_ui.
+  std::unique_ptr<AnnotatorPageHandlerImpl> handler_;
 };
 
 }  // namespace ash
diff --git a/ash/wm/multitask_menu_nudge_delegate_ash.cc b/ash/wm/multitask_menu_nudge_delegate_ash.cc
index be4445e..de18787 100644
--- a/ash/wm/multitask_menu_nudge_delegate_ash.cc
+++ b/ash/wm/multitask_menu_nudge_delegate_ash.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/multitask_menu_nudge_delegate_ash.h"
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_multitask_cue.h"
@@ -18,17 +19,13 @@
 }
 
 std::string GetShowCountPrefName(bool tablet_mode) {
-  return tablet_mode
-             ? chromeos::MultitaskMenuNudgeController::kTabletShownCountPrefName
-             : chromeos::MultitaskMenuNudgeController::
-                   kClamshellShownCountPrefName;
+  return tablet_mode ? prefs::kMultitaskMenuNudgeTabletShownCount
+                     : prefs::kMultitaskMenuNudgeClamshellShownCount;
 }
 
 std::string GetLastShownPrefName(bool tablet_mode) {
-  return tablet_mode
-             ? chromeos::MultitaskMenuNudgeController::kTabletLastShownPrefName
-             : chromeos::MultitaskMenuNudgeController::
-                   kClamshellLastShownPrefName;
+  return tablet_mode ? prefs::kMultitaskMenuNudgeTabletLastShown
+                     : prefs::kMultitaskMenuNudgeClamshellLastShown;
 }
 
 }  // namespace
diff --git a/base/base_switches.cc b/base/base_switches.cc
index f396198b..88613473 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -128,7 +128,7 @@
 // Disable high-resolution timer on Windows.
 const char kDisableHighResTimer[] = "disable-highres-timer";
 
-// Disables the USB keyboard detection for blocking the OSK on Win8+.
+// Disables the USB keyboard detection for blocking the OSK on Windows.
 const char kDisableUsbKeyboardDetect[]      = "disable-usb-keyboard-detect";
 #endif
 
diff --git a/base/command_line.cc b/base/command_line.cc
index ae845dc..a5ccbce 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -484,7 +484,7 @@
   int num_args = 0;
   wchar_t** args = NULL;
   // When calling CommandLineToArgvW, use the apiset if available.
-  // Doing so will bypass loading shell32.dll on Win8+.
+  // Doing so will bypass loading shell32.dll on Windows.
   HMODULE downlevel_shell32_dll =
       ::LoadLibraryEx(L"api-ms-win-downlevel-shell32-l1-1-0.dll", nullptr,
                       LOAD_LIBRARY_SEARCH_SYSTEM32);
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc
index 5b77bc2..83d51e9 100644
--- a/base/json/json_reader.cc
+++ b/base/json/json_reader.cc
@@ -148,14 +148,6 @@
 }
 
 // static
-std::unique_ptr<Value> JSONReader::ReadDeprecated(StringPiece json,
-                                                  int options,
-                                                  size_t max_depth) {
-  absl::optional<Value> value = Read(json, options, max_depth);
-  return value ? Value::ToUniquePtrValue(std::move(*value)) : nullptr;
-}
-
-// static
 JSONReader::Result JSONReader::ReadAndReturnValueWithError(StringPiece json,
                                                            int options) {
 #if BUILDFLAG(BUILD_RUST_JSON_READER)
diff --git a/base/json/json_reader.h b/base/json/json_reader.h
index 466bff3c..00d23cfc 100644
--- a/base/json/json_reader.h
+++ b/base/json/json_reader.h
@@ -36,7 +36,6 @@
 #ifndef BASE_JSON_JSON_READER_H_
 #define BASE_JSON_JSON_READER_H_
 
-#include <memory>
 #include <string>
 
 #include "base/base_export.h"
@@ -107,16 +106,6 @@
       int options = JSON_PARSE_CHROMIUM_EXTENSIONS,
       size_t max_depth = internal::kAbsoluteMaxDepth);
 
-  // Deprecated. Use the Read() method above.
-  // Reads and parses |json|, returning a Value.
-  // If |json| is not a properly formed JSON string, returns nullptr.
-  // Wrap this in base::FooValue::From() to check the Value is of type Foo and
-  // convert to a FooValue at the same time.
-  static std::unique_ptr<Value> ReadDeprecated(
-      StringPiece json,
-      int options = JSON_PARSE_CHROMIUM_EXTENSIONS,
-      size_t max_depth = internal::kAbsoluteMaxDepth);
-
   // Reads and parses |json| like Read(). On success returns a Value as the
   // expected value. Otherwise, it returns an Error instance, populated with a
   // formatted error message, an error code, and the error location if
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 98f387d..ff7025f 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_param_associator.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
@@ -642,6 +643,9 @@
   DCHECK(global_->create_trials_from_command_line_called_);
 
   if (!global_->field_trial_allocator_) {
+    UmaHistogramBoolean(
+        "ChildProcess.FieldTrials.GetInitiallyActiveFieldTrials.FromString",
+        true);
     GetActiveFieldTrialGroupsFromString(
         command_line.GetSwitchValueASCII(switches::kForceFieldTrials),
         active_groups);
@@ -738,6 +742,8 @@
 
   // If the readonly handle did not get created, fall back to flags.
   if (!global_ || !global_->readonly_allocator_region_.IsValid()) {
+    UmaHistogramBoolean(
+        "ChildProcess.FieldTrials.PopulateLaunchOptions.CommandLine", true);
     AddFeatureAndFieldTrialFlags(command_line);
     return;
   }
diff --git a/base/task/sequence_manager/thread_controller.cc b/base/task/sequence_manager/thread_controller.cc
index 6157f374..d96b50e 100644
--- a/base/task/sequence_manager/thread_controller.cc
+++ b/base/task/sequence_manager/thread_controller.cc
@@ -61,8 +61,14 @@
       base::HistogramBase::kUmaTargetedHistogramFlag);
 
 #if BUILDFLAG(ENABLE_BASE_TRACING)
-  perfetto_track_.emplace(reinterpret_cast<uint64_t>(this),
-                          perfetto::ThreadTrack::Current());
+  perfetto_track_.emplace(
+      reinterpret_cast<uint64_t>(this),
+      // TODO(crbug.com/1006541): Replace with ThreadTrack::Current() after SDK
+      // migration.
+      // In the non-SDK version, ThreadTrack::Current() returns a different
+      // track id on some platforms (for example Mac OS), which results in
+      // async tracks not being associated with their thread.
+      perfetto::ThreadTrack::ForThread(base::PlatformThread::CurrentId()));
   // TODO(1006541): Use Perfetto library to name this Track.
   // auto desc = perfetto_track_->Serialize();
   // desc.set_name(JoinString({"MessagePumpPhases", thread_name}, " "));
diff --git a/base/threading/platform_thread_win.h b/base/threading/platform_thread_win.h
index b06e773..18a7260 100644
--- a/base/threading/platform_thread_win.h
+++ b/base/threading/platform_thread_win.h
@@ -14,9 +14,8 @@
 namespace base {
 namespace internal {
 
-// Assert that the memory priority of |thread| is |memory_priority|. No-op on
-// Windows 7 because ::GetThreadInformation() is not available. Exposed for unit
-// tests.
+// Assert that the memory priority of `thread` is `memory_priority`. Exposed
+// for unit tests.
 BASE_EXPORT void AssertMemoryPriority(HANDLE thread, int memory_priority);
 
 }  // namespace internal
diff --git a/base/trace_event/trace_logging_minimal_win.h b/base/trace_event/trace_logging_minimal_win.h
index f2c830c..346e6877 100644
--- a/base/trace_event/trace_logging_minimal_win.h
+++ b/base/trace_event/trace_logging_minimal_win.h
@@ -346,49 +346,43 @@
 // Helper for creating event descriptors for use with WriteEvent.
 constexpr EVENT_DESCRIPTOR TlmEventDescriptor(uint8_t level,
                                               uint64_t keyword) noexcept {
-  return {
-      // Id
-      // TraceLogging generally uses the event's Name instead of Id+Version,
-      // so Id is normally set to 0 for TraceLogging events.
-      0,
+  return {// Id
+          // TraceLogging generally uses the event's Name instead of Id+Version,
+          // so Id is normally set to 0 for TraceLogging events.
+          0,
 
-      // Version
-      // TraceLogging generally uses the event's Name instead of Id+Version,
-      // so Version is normally set to 0 for TraceLogging events.
-      0,
+          // Version
+          // TraceLogging generally uses the event's Name instead of Id+Version,
+          // so Version is normally set to 0 for TraceLogging events.
+          0,
 
-      // Channel (WINEVENT_CHANNEL_*)
-      // Setting Channel = 11 allows TraceLogging events to be decoded
-      // correctly even if they were collected on older operating systems.
-      // If a TraceLogging event sets channel to a value other than 11, the
-      // event will only decode correctly if it was collected on an
-      // operating system that has built-in TraceLogging support, i.e.
-      // Windows 7sp1 + patch, Windows 8.1 + patch, or Windows 10+.
-      11,  // = WINEVENT_CHANNEL_TRACELOGGING
+          // Channel (WINEVENT_CHANNEL_*)
+          // TraceLogging-based events normally use channel 11.
+          11,  // = WINEVENT_CHANNEL_TRACELOGGING
 
-      // Level (WINEVENT_LEVEL_*)
-      // 0=always, 1=fatal, 2=error, 3=warning, 4=info, 5=verbose.
-      // Levels higher than 5 are for user-defined debug levels.
-      level,
+          // Level (WINEVENT_LEVEL_*)
+          // 0=always, 1=fatal, 2=error, 3=warning, 4=info, 5=verbose.
+          // Levels higher than 5 are for user-defined debug levels.
+          level,
 
-      // Opcode (WINEVENT_OPCODE_*)
-      // Set Opcode for special semantics such as starting/ending an
-      // activity.
-      0,  // = WINEVENT_OPCODE_INFO
+          // Opcode (WINEVENT_OPCODE_*)
+          // Set Opcode for special semantics such as starting/ending an
+          // activity.
+          0,  // = WINEVENT_OPCODE_INFO
 
-      // Task
-      // Set Task for user-defined semantics.
-      0,  // = WINEVENT_TASK_NONE
+          // Task
+          // Set Task for user-defined semantics.
+          0,  // = WINEVENT_TASK_NONE
 
-      // Keyword
-      // A keyword is a 64-bit value used for filtering events. Each bit of
-      // the keyword indicates whether the event belongs to a particular
-      // category of events. The top 16 bits of keyword have
-      // Microsoft-defined semantics and should be set to 0. The low 48 bits
-      // of keyword have user-defined semantics. All events should use a
-      // nonzero keyword to support effective event filtering (events with
-      // keyword set to 0 always pass keyword filtering).
-      keyword};
+          // Keyword
+          // A keyword is a 64-bit value used for filtering events. Each bit of
+          // the keyword indicates whether the event belongs to a particular
+          // category of events. The top 16 bits of keyword have
+          // Microsoft-defined semantics and should be set to 0. The low 48 bits
+          // of keyword have user-defined semantics. All events should use a
+          // nonzero keyword to support effective event filtering (events with
+          // keyword set to 0 always pass keyword filtering).
+          keyword};
 }
 
 #endif  // BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_
diff --git a/base/win/pe_image.h b/base/win/pe_image.h
index 7f3b0115f..b697493 100644
--- a/base/win/pe_image.h
+++ b/base/win/pe_image.h
@@ -15,10 +15,6 @@
 
 #include <stdint.h>
 
-#if defined(_WIN32_WINNT_WIN8)
-// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h.
-#undef FACILITY_VISUALCPP
-#endif
 #include <DelayIMP.h>
 
 namespace base {
diff --git a/base/win/shortcut.h b/base/win/shortcut.h
index 53b4b14..25f035b 100644
--- a/base/win/shortcut.h
+++ b/base/win/shortcut.h
@@ -105,7 +105,7 @@
   int icon_index = -1;
   // The app model id for the shortcut.
   std::wstring app_id;
-  // Whether this is a dual mode shortcut (Win8+).
+  // Whether this is a dual mode shortcut (Windows).
   bool dual_mode = false;
   // The CLSID of the COM object registered with the OS via the shortcut. This
   // is for app activation via user interaction with a toast notification in the
diff --git a/base/win/win_util.h b/base/win/win_util.h
index c50b3c2..7ad82fc 100644
--- a/base/win/win_util.h
+++ b/base/win/win_util.h
@@ -83,9 +83,8 @@
                                           const PROPERTYKEY& property_key,
                                           const CLSID& property_clsid_value);
 
-// Sets the application id in given IPropertyStore. The function is intended
-// for tagging application/chromium shortcut, browser window and jump list for
-// Win7.
+// Sets the application id in given IPropertyStore. The function is used to tag
+// application/Chrome shortcuts, and set app details for Chrome windows.
 BASE_EXPORT bool SetAppIdForPropertyStore(IPropertyStore* property_store,
                                           const wchar_t* app_id);
 
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index a542f336..692e7adf 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -285,10 +285,7 @@
   }
 }
 
-# Chromium supports running on Windows 7, but if these constants are set to
-# Windows 7, then newer APIs aren't made available by the Windows SDK.
-# So we set this to Windows 10 and then are careful to check at runtime
-# to only call newer APIs when they're available.
+# Chromium only supports Windowes 10+.
 # Some third-party libraries assume that these defines set what version of
 # Windows is available at runtime. Targets using these libraries need to
 # manually override this config for their compiles.
diff --git a/cc/slim/frame_sink_impl.cc b/cc/slim/frame_sink_impl.cc
index 7776c3f8..48a267e5 100644
--- a/cc/slim/frame_sink_impl.cc
+++ b/cc/slim/frame_sink_impl.cc
@@ -53,7 +53,10 @@
       io_thread_id_(io_thread_id) {}
 
 FrameSinkImpl::~FrameSinkImpl() {
-  for (const auto& uploaded_resource_pair : uploaded_resources_) {
+  // Iterate a copy of `uploaded_resources_` since it might be modified
+  // when `UIResourceReleased()` is called.
+  for (const auto& uploaded_resource_pair :
+       UploadedResourceMap(uploaded_resources_)) {
     resource_provider_.RemoveImportedResource(
         uploaded_resource_pair.second.viz_resource_id);
   }
@@ -160,6 +163,7 @@
   uploaded_resource.size = resource_bitmap.GetSize();
   uploaded_resource.is_opaque = resource_bitmap.GetOpaque();
 
+  DCHECK(!uploaded_resources_.contains(resource_id));
   uploaded_resources_.emplace(resource_id, uploaded_resource);
 }
 
diff --git a/cc/slim/layer_tree_cc_wrapper.cc b/cc/slim/layer_tree_cc_wrapper.cc
index 3322b450..46a1f77 100644
--- a/cc/slim/layer_tree_cc_wrapper.cc
+++ b/cc/slim/layer_tree_cc_wrapper.cc
@@ -76,7 +76,9 @@
       cc::LayerTreeHost::CreateSingleThreaded(this, std::move(cc_init_params));
 }
 
-LayerTreeCcWrapper::~LayerTreeCcWrapper() = default;
+LayerTreeCcWrapper::~LayerTreeCcWrapper() {
+  SetRoot(nullptr);
+}
 
 cc::UIResourceManager* LayerTreeCcWrapper::GetUIResourceManager() {
   return host_->GetUIResourceManager();
@@ -150,9 +152,13 @@
     root_->SetLayerTree(nullptr);
   }
   root_ = std::move(root);
-  root_->SetLayerTree(this);
-  DCHECK(root_->cc_layer_);
-  host_->SetRootLayer(root_->cc_layer_);
+  if (root_) {
+    root_->SetLayerTree(this);
+    DCHECK(root_->cc_layer_);
+    host_->SetRootLayer(root_->cc_layer_);
+  } else {
+    host_->SetRootLayer(nullptr);
+  }
 }
 
 void LayerTreeCcWrapper::SetFrameSink(std::unique_ptr<FrameSink> sink) {
diff --git a/cc/slim/layer_tree_impl.cc b/cc/slim/layer_tree_impl.cc
index 815dabea..0eff70f 100644
--- a/cc/slim/layer_tree_impl.cc
+++ b/cc/slim/layer_tree_impl.cc
@@ -43,7 +43,9 @@
 
 LayerTreeImpl::LayerTreeImpl(LayerTreeClient* client) : client_(client) {}
 
-LayerTreeImpl::~LayerTreeImpl() = default;
+LayerTreeImpl::~LayerTreeImpl() {
+  SetRoot(nullptr);
+}
 
 cc::UIResourceManager* LayerTreeImpl::GetUIResourceManager() {
   return &ui_resource_manager_;
@@ -116,6 +118,7 @@
     }
   }
   copy_requests_for_next_frame_.push_back(std::move(request));
+  SetNeedsDraw();
 }
 
 base::OnceClosure LayerTreeImpl::DeferBeginFrame() {
diff --git a/cc/slim/slim_layer_tree_compositor_frame_unittest.cc b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
index 23b1e24..0680efd 100644
--- a/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
+++ b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
@@ -500,6 +500,10 @@
   auto solid_color_layer =
       CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
   layer_tree_->SetRoot(solid_color_layer);
+  {
+    viz::CompositorFrame frame = ProduceFrame();
+    EXPECT_FALSE(layer_tree_->NeedsBeginFrames());
+  }
 
   auto copy_request_no_source_1 = std::make_unique<viz::CopyOutputRequest>(
       viz::CopyOutputRequest::ResultFormat::RGBA,
@@ -531,6 +535,7 @@
   copy_request_with_difference_source->set_source(token2);
 
   layer_tree_->RequestCopyOfOutput(std::move(copy_request_no_source_1));
+  EXPECT_TRUE(layer_tree_->NeedsBeginFrames());
   layer_tree_->RequestCopyOfOutput(std::move(copy_request_no_source_2));
   layer_tree_->RequestCopyOfOutput(std::move(copy_request_with_source));
   layer_tree_->RequestCopyOfOutput(std::move(copy_request_with_same_source));
@@ -635,6 +640,50 @@
   }
 }
 
+TEST_F(SlimLayerTreeCompositorFrameTest, ReclaimResources) {
+  constexpr size_t kNumLayers = 6;
+  std::vector<scoped_refptr<UIResourceLayer>> layers;
+  for (size_t i = 0; i < kNumLayers; ++i) {
+    layers.push_back(UIResourceLayer::Create());
+    layers[i]->SetBounds(viewport_.size());
+    layers[i]->SetIsDrawable(true);
+    if (i == 0u) {
+      layer_tree_->SetRoot(layers[i]);
+    } else {
+      layers[i - 1]->AddChild(layers[i]);
+    }
+
+    auto image_info =
+        SkImageInfo::Make(1, 1, kN32_SkColorType, kPremul_SkAlphaType);
+    SkBitmap bitmap;
+    bitmap.allocPixels(image_info);
+    bitmap.setImmutable();
+    layers[i]->SetBitmap(bitmap);
+  }
+
+  viz::CompositorFrame frame = ProduceFrame();
+  EXPECT_EQ(frame.resource_list.size(), kNumLayers);
+  for (size_t i = 0; i < kNumLayers; ++i) {
+    EXPECT_TRUE(frame_sink_->client_resource_provider()->InUseByConsumer(
+        frame.resource_list[i].id));
+  }
+
+  // Return every other resource.
+  std::vector<viz::ReturnedResource> returned_resources;
+  for (size_t i = 0; i < kNumLayers; i += 2) {
+    returned_resources.push_back(frame.resource_list[i].ToReturnedResource());
+  }
+  frame_sink_->ReclaimResources(std::move(returned_resources));
+  for (size_t i = 0; i < kNumLayers; i += 2) {
+    EXPECT_FALSE(frame_sink_->client_resource_provider()->InUseByConsumer(
+        frame.resource_list[i].id));
+  }
+  for (size_t i = 1; i < kNumLayers; i += 2) {
+    EXPECT_TRUE(frame_sink_->client_resource_provider()->InUseByConsumer(
+        frame.resource_list[i].id));
+  }
+}
+
 TEST_F(SlimLayerTreeCompositorFrameTest, NinePatchLayerAppendQuads) {
   auto nine_patch_layer = NinePatchLayer::Create();
   nine_patch_layer->SetBounds(viewport_.size());
diff --git a/cc/slim/slim_layer_tree_unittest.cc b/cc/slim/slim_layer_tree_unittest.cc
index 50f24300..6bfcef2 100644
--- a/cc/slim/slim_layer_tree_unittest.cc
+++ b/cc/slim/slim_layer_tree_unittest.cc
@@ -234,6 +234,27 @@
             std::vector<viz::SurfaceRange>());
 }
 
+TEST_F(SlimLayerTreeTest, DestroyTreeBeforeLayer) {
+  // Regression test for use after free.
+  auto root_layer = Layer::Create();
+
+  // Use SurfaceLayer here because it accesses LayerTreeImpl pointer in
+  // SetLayerTree.
+  auto surface_layer = SurfaceLayer::Create();
+  root_layer->AddChild(surface_layer);
+  base::UnguessableToken token = base::UnguessableToken::Create();
+  viz::SurfaceId end(viz::FrameSinkId(1u, 2u),
+                     viz::LocalSurfaceId(5u, 6u, token));
+  surface_layer->SetSurfaceId(end, cc::DeadlinePolicy::UseDefaultDeadline());
+
+  layer_tree_->SetRoot(root_layer);
+
+  layer_tree_.reset();
+
+  EXPECT_EQ(root_layer->layer_tree(), nullptr);
+  EXPECT_EQ(surface_layer->layer_tree(), nullptr);
+}
+
 }  // namespace
 
 }  // namespace cc::slim
diff --git a/cc/slim/surface_layer.cc b/cc/slim/surface_layer.cc
index 690fae9..232b808 100644
--- a/cc/slim/surface_layer.cc
+++ b/cc/slim/surface_layer.cc
@@ -121,6 +121,10 @@
     return;
   }
 
+  if (layer_tree() == tree) {
+    return;
+  }
+
   if (layer_tree() && surface_range_.IsValid()) {
     static_cast<LayerTreeImpl*>(layer_tree())
         ->RemoveSurfaceRange(surface_range_);
diff --git a/chrome/VERSION b/chrome/VERSION
index 8a7b4b7..faa4e64 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=113
 MINOR=0
-BUILD=5621
+BUILD=5622
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index c38a8877..9703e28 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -414,11 +414,11 @@
       "//components/commerce/core/android:core_java",
       "//components/component_updater/android:background_task_update_scheduler_java",
       "//components/content_capture/android:java",
+      "//components/content_relationship_verification/android:java",
       "//components/content_settings/android:content_settings_enums_java",
       "//components/content_settings/android:java",
       "//components/crash/android:anr_collector_java",
       "//components/crash/android:java",
-      "//components/digital_asset_links/android:java",
       "//components/digital_goods/mojom:mojom_java",
       "//components/dom_distiller/content/browser/android:dom_distiller_content_java",
       "//components/dom_distiller/core/android:dom_distiller_core_java",
@@ -770,6 +770,7 @@
       "//chrome/browser/android/policy/policy_auditor.cc",
       "//chrome/browser/android/webapk/webapk_installer.h",
       "//chrome/browser/long_screenshots/long_screenshots_tab_service.h",
+      "//chrome/browser/metrics/metrics_reporting_state.h",
       "//chrome/browser/notifications/notification_handler.h",
       "//chrome/browser/notifications/notification_platform_bridge_android.cc",
     ]
@@ -1024,9 +1025,9 @@
       "//components/commerce/core:proto_java",
       "//components/commerce/core/android:core_java",
       "//components/content_capture/android:java",
+      "//components/content_relationship_verification/android:java",
+      "//components/content_relationship_verification/android:junit_test_support",
       "//components/content_settings/android:content_settings_enums_java",
-      "//components/digital_asset_links/android:java",
-      "//components/digital_asset_links/android:junit_test_support",
       "//components/digital_goods/mojom:mojom_java",
       "//components/dom_distiller/core/android:dom_distiller_core_java",
       "//components/embedder_support/android:content_view_java",
@@ -3725,7 +3726,7 @@
     "//chrome/browser/ui/messages/android:jni_headers",
     "//chrome/browser/util:jni_headers",
     "//chrome/browser/webauthn/android:jni_headers",
-    "//components/digital_asset_links/android:jni_headers",
+    "//components/content_relationship_verification/android:jni_headers",
     "//components/image_fetcher:jni_headers",
     "//components/media_router/browser/android:jni_headers",
     "//components/ukm/android:jni_headers",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index 4a72046b..86ffa34 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.app.Activity;
-import android.content.Context;
 import android.graphics.Rect;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
@@ -61,11 +60,13 @@
             TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
             TabGridDialogMediator.AnimationSourceViewProvider animationSourceViewProvider,
             Supplier<ShareDelegate> shareDelegateSupplier, ScrimCoordinator scrimCoordinator,
-            ViewGroup rootView) {
+            TabGroupTitleEditor tabGroupTitleEditor, ViewGroup rootView) {
         try (TraceEvent e = TraceEvent.scoped("TabGridDialogCoordinator.constructor")) {
             mActivity = activity;
             mComponentName = animationSourceViewProvider == null ? "TabGridDialogFromStrip"
                                                                  : "TabGridDialogInSwitcher";
+            mTabModelSelector = tabModelSelector;
+            mTabContentManager = tabContentManager;
 
             mModel = new PropertyModel(TabGridPanelProperties.ALL_KEYS);
             mRootView = rootView;
@@ -119,15 +120,10 @@
                     TabGridPanelViewBinder::bind);
             mBackPressChangedSupplier.set(isVisible());
             mModel.addObserver((source, key) -> mBackPressChangedSupplier.set(isVisible()));
-        }
-    }
 
-    public void initWithNative(Context context, TabModelSelector tabModelSelector,
-            TabContentManager tabContentManager, TabGroupTitleEditor tabGroupTitleEditor) {
-        try (TraceEvent e = TraceEvent.scoped("TabGridDialogCoordinator.initWithNative")) {
-            mTabModelSelector = tabModelSelector;
-            mTabContentManager = tabContentManager;
-
+            // This is always created post-native so calling these immediately is safe.
+            // TODO(crbug/1418690): Consider inlining these behaviors in their respective
+            // constructors if possible.
             mMediator.initWithNative(this::getTabSelectionEditorController, tabGroupTitleEditor);
             mTabListCoordinator.initWithNative(null);
         }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 8dfdf7b..65b9b41 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -81,6 +81,7 @@
     private final TabContentManager mTabContentManager;
     private PropertyModelChangeProcessor mModelChangeProcessor;
     private TabGridDialogCoordinator mTabGridDialogCoordinator;
+    private Supplier<TabGridDialogMediator.DialogController> mTabGridDialogControllerSupplier;
     private TabListCoordinator mTabStripCoordinator;
     private TabGroupUiMediator mMediator;
 
@@ -127,6 +128,16 @@
         }
     }
 
+    private void initTabGridDialogCoordinator() {
+        assert mTabGridDialogControllerSupplier != null;
+        if (mTabGridDialogCoordinator != null) return;
+
+        mTabGridDialogCoordinator = new TabGridDialogCoordinator(mActivity, mTabModelSelector,
+                mTabContentManager, mTabCreatorManager, mActivity.findViewById(R.id.coordinator),
+                null, null, null, mShareDelegateSupplier, mScrimCoordinator,
+                mTabStripCoordinator.getTabGroupTitleEditor(), mRootView);
+    }
+
     /**
      * Handle any initialization that occurs once native has been loaded.
      */
@@ -154,21 +165,29 @@
 
             // TODO(crbug.com/972217): find a way to enable interactions between grid tab switcher
             //  and the dialog here.
-            TabGridDialogMediator.DialogController dialogController = null;
             if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(activity)
                     && mScrimCoordinator != null) {
-                mTabGridDialogCoordinator = new TabGridDialogCoordinator(mActivity,
-                        mTabModelSelector, mTabContentManager, mTabCreatorManager,
-                        mActivity.findViewById(R.id.coordinator), null, null, null,
-                        mShareDelegateSupplier, mScrimCoordinator, mRootView);
-                mTabGridDialogCoordinator.initWithNative(mContext, mTabModelSelector,
-                        mTabContentManager, mTabStripCoordinator.getTabGroupTitleEditor());
-                dialogController = mTabGridDialogCoordinator.getDialogController();
+                mTabGridDialogControllerSupplier =
+                        new Supplier<TabGridDialogMediator.DialogController>() {
+                            @Override
+                            public TabGridDialogMediator.DialogController get() {
+                                initTabGridDialogCoordinator();
+                                return mTabGridDialogCoordinator.getDialogController();
+                            }
+
+                            @Override
+                            public boolean hasValue() {
+                                return mTabGridDialogCoordinator != null;
+                            }
+                        };
+            } else {
+                mTabGridDialogControllerSupplier = null;
             }
 
             mMediator = new TabGroupUiMediator(mActivity, visibilityController, this, mModel,
                     mTabModelSelector, mTabCreatorManager, mLayoutStateProviderSupplier,
-                    mIncognitoStateProvider, dialogController, mOmniboxFocusStateSupplier);
+                    mIncognitoStateProvider, mTabGridDialogControllerSupplier,
+                    mOmniboxFocusStateSupplier);
 
             TabGroupUtils.startObservingForCreationIPH();
 
@@ -220,8 +239,8 @@
      */
     @Override
     public void resetGridWithListOfTabs(List<Tab> tabs) {
-        if (mTabGridDialogCoordinator != null) {
-            mTabGridDialogCoordinator.resetWithListOfTabs(tabs);
+        if (mTabGridDialogControllerSupplier != null) {
+            mTabGridDialogControllerSupplier.get().resetWithListOfTabs(tabs);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index 855bfb3..65718075a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -17,6 +17,7 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -99,7 +100,7 @@
     private final BottomControlsCoordinator
             .BottomControlsVisibilityController mVisibilityController;
     private final IncognitoStateProvider mIncognitoStateProvider;
-    private final TabGridDialogMediator.DialogController mTabGridDialogController;
+    private final Supplier<TabGridDialogMediator.DialogController> mTabGridDialogControllerSupplier;
     private final IncognitoStateObserver mIncognitoStateObserver;
     private final TabModelSelectorObserver mTabModelSelectorObserver;
     private final ObservableSupplier<Boolean> mOmniboxFocusStateSupplier;
@@ -119,7 +120,7 @@
             TabCreatorManager tabCreatorManager,
             OneshotSupplier<LayoutStateProvider> layoutStateProviderSupplier,
             IncognitoStateProvider incognitoStateProvider,
-            @Nullable TabGridDialogMediator.DialogController dialogController,
+            @Nullable Supplier<TabGridDialogMediator.DialogController> dialogControllerSupplier,
             ObservableSupplier<Boolean> omniboxFocusStateSupplier) {
         mContext = context;
         mResetHandler = resetHandler;
@@ -128,7 +129,7 @@
         mTabCreatorManager = tabCreatorManager;
         mVisibilityController = visibilityController;
         mIncognitoStateProvider = incognitoStateProvider;
-        mTabGridDialogController = dialogController;
+        mTabGridDialogControllerSupplier = dialogControllerSupplier;
         mOmniboxFocusStateSupplier = omniboxFocusStateSupplier;
 
         if (layoutStateProviderSupplier.get() != null
@@ -412,21 +413,27 @@
     public boolean onBackPressed() {
         // TODO(crbug.com/1006421): add a regression test to make sure that the back button closes
         // the dialog when the dialog is showing.
-        return mTabGridDialogController != null && mTabGridDialogController.handleBackPressed();
+        return mTabGridDialogControllerSupplier != null
+                && mTabGridDialogControllerSupplier.hasValue()
+                && mTabGridDialogControllerSupplier.get().handleBackPressed();
     }
 
     @Override
     public @BackPressResult int handleBackPress() {
-        if (mTabGridDialogController != null) return mTabGridDialogController.handleBackPress();
+        if (mTabGridDialogControllerSupplier != null
+                && mTabGridDialogControllerSupplier.hasValue()) {
+            return mTabGridDialogControllerSupplier.get().handleBackPress();
+        }
         return BackPressResult.FAILURE;
     }
 
     @Override
     public ObservableSupplier<Boolean> getHandleBackPressChangedSupplier() {
-        if (mTabGridDialogController == null) {
+        if (mTabGridDialogControllerSupplier == null
+                || !mTabGridDialogControllerSupplier.hasValue()) {
             return BackPressHandler.super.getHandleBackPressChangedSupplier();
         }
-        return mTabGridDialogController.getHandleBackPressChangedSupplier();
+        return mTabGridDialogControllerSupplier.get().getHandleBackPressChangedSupplier();
     }
 
     public void destroy() {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 599e4f7..d2c78ec 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -19,6 +19,8 @@
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import org.chromium.base.Callback;
+import org.chromium.base.Promise;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
@@ -119,8 +121,10 @@
     private final TabListCoordinator mTabListCoordinator;
     private final TabSwitcherMediator mMediator;
     private final MultiThumbnailCardProvider mMultiThumbnailCardProvider;
+    private final ScrimCoordinator mGridDialogScrimCoordinator;
+    private final boolean mUsesTabGridDialogCoordinator;
     @Nullable
-    private final TabGridDialogCoordinator mTabGridDialogCoordinator;
+    private TabGridDialogCoordinator mTabGridDialogCoordinator;
     private final TabModelSelector mTabModelSelector;
     private final @TabListCoordinator.TabListMode int mMode;
     private final MessageCardProviderCoordinator mMessageCardProviderCoordinator;
@@ -198,10 +202,58 @@
             PropertyModel containerViewModel =
                     new PropertyModel(TabListContainerProperties.ALL_KEYS);
 
+            OneshotSupplier<TabGridDialogMediator.DialogController> dialogControllerSupplier = null;
+            if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(activity)) {
+                mGridDialogScrimCoordinator =
+                        shouldUseNewScrim() ? createScrimCoordinator() : scrimCoordinator;
+                mUsesTabGridDialogCoordinator = true;
+                dialogControllerSupplier =
+                        new OneshotSupplier<TabGridDialogMediator.DialogController>() {
+                            // Implementation is based on OneshotSupplierImpl with modifications
+                            // such that onAvailable does not invoke get() unless the object already
+                            // exists this prevents callers of onAvailable from triggering the lazy
+                            // creation of the TabGridDialogCoordinator before it is required.
+                            private final Promise<TabGridDialogMediator.DialogController> mPromise =
+                                    new Promise<>();
+                            private final ThreadUtils.ThreadChecker mThreadChecker =
+                                    new ThreadUtils.ThreadChecker();
+
+                            @Override
+                            public TabGridDialogMediator.DialogController onAvailable(
+                                    Callback<TabGridDialogMediator.DialogController> callback) {
+                                mThreadChecker.assertOnValidThread();
+                                mPromise.then(callback);
+                                if (!hasValue()) return null;
+
+                                return get();
+                            }
+
+                            @Override
+                            public TabGridDialogMediator.DialogController get() {
+                                mThreadChecker.assertOnValidThread();
+                                if (initTabGridDialogCoordinator()) {
+                                    assert !mPromise.isFulfilled();
+                                    mPromise.fulfill(
+                                            mTabGridDialogCoordinator.getDialogController());
+                                }
+                                assert mPromise.isFulfilled();
+                                return mPromise.getResult();
+                            }
+
+                            @Override
+                            public boolean hasValue() {
+                                return mTabGridDialogCoordinator != null;
+                            }
+                        };
+            } else {
+                mGridDialogScrimCoordinator = null;
+                mUsesTabGridDialogCoordinator = false;
+                mTabGridDialogCoordinator = null;
+            }
             mMediator = new TabSwitcherMediator(activity, this, containerViewModel,
                     tabModelSelector, browserControls, container, tabContentManager, this, this,
                     multiWindowModeStateDispatcher, mode, incognitoReauthControllerSupplier,
-                    backPressManager);
+                    backPressManager, dialogControllerSupplier);
 
             mTabSwitcherCustomViewManager = new TabSwitcherCustomViewManager(mMediator);
 
@@ -275,19 +327,6 @@
                         }
                     });
 
-            if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(activity)) {
-                ScrimCoordinator gridDialogScrimCoordinator =
-                        shouldUseNewScrim() ? createScrimCoordinator() : scrimCoordinator;
-                mTabGridDialogCoordinator = new TabGridDialogCoordinator(activity, tabModelSelector,
-                        tabContentManager, tabCreatorManager, mCoordinatorView, this, mMediator,
-                        this::getTabGridDialogAnimationSourceView, shareDelegateSupplier,
-                        gridDialogScrimCoordinator, rootView);
-                mMediator.setTabGridDialogController(
-                        mTabGridDialogCoordinator.getDialogController());
-            } else {
-                mTabGridDialogCoordinator = null;
-            }
-
             mMenuOrKeyboardActionController = menuOrKeyboardActionController;
 
             if (mode == TabListCoordinator.TabListMode.GRID) {
@@ -369,6 +408,22 @@
     }
 
     /**
+     * @return false if already initialized or true when first initialized.
+     */
+    private boolean initTabGridDialogCoordinator() {
+        assert mUsesTabGridDialogCoordinator;
+        if (mTabGridDialogCoordinator != null) return false;
+
+        mTabGridDialogCoordinator =
+                new TabGridDialogCoordinator(mActivity, mTabModelSelector, mTabContentManager,
+                        mTabCreatorManager, mCoordinatorView, TabSwitcherCoordinator.this,
+                        mMediator, TabSwitcherCoordinator.this::getTabGridDialogAnimationSourceView,
+                        mShareDelegateSupplier, mGridDialogScrimCoordinator,
+                        mTabListCoordinator.getTabGroupTitleEditor(), mRootView);
+        return true;
+    }
+
+    /**
      * Tablet Tab Switcher polish uses a scrim to show/hide tab switcher.
      * Create a new scrim via a new scrim coordinator for tab group dialog.
      * @return if tab switcher polish is enabled on tablets.
@@ -416,11 +471,6 @@
                     setUpTabGroupManualSelectionMode(mActivity);
                 }
             }
-            if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(mActivity)
-                    && mTabGridDialogCoordinator != null) {
-                mTabGridDialogCoordinator.initWithNative(mActivity, mTabModelSelector,
-                        mTabContentManager, mTabListCoordinator.getTabGroupTitleEditor());
-            }
 
             final TabSelectionEditorController controller = mTabSelectionEditorCoordinator != null
                     ? mTabSelectionEditorCoordinator.getController()
@@ -574,8 +624,15 @@
 
     @Override
     public Supplier<Boolean> getTabGridDialogVisibilitySupplier() {
-        if (mTabGridDialogCoordinator != null) {
-            return mTabGridDialogCoordinator::isVisible;
+        if (mUsesTabGridDialogCoordinator) {
+            // mTabGridDialogCoordinator is lazily created when first displaying something in the
+            // dialog. Return false until it has shown something.
+            return () -> {
+                if (mTabGridDialogCoordinator != null) {
+                    return mTabGridDialogCoordinator.isVisible();
+                }
+                return false;
+            };
         }
         return () -> false;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 3d45856..df98be4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -149,7 +149,8 @@
     private CallbackController mCallbackController;
     private Integer mSoftCleanupDelayMsForTesting;
     private Integer mCleanupDelayMsForTesting;
-    private TabGridDialogMediator.DialogController mTabGridDialogController;
+    private OneshotSupplier<TabGridDialogMediator.DialogController>
+            mTabGridDialogControllerSupplier;
     private TabSelectionEditorCoordinator
             .TabSelectionEditorController mTabSelectionEditorController;
     private TabSwitcher.OnTabSelectingListener mOnTabSelectingListener;
@@ -280,6 +281,8 @@
      * @param incognitoReauthControllerSupplier {@link OneshotSupplier<IncognitoReauthController>}
      *         to detect pending re-auth when tab switcher is shown.
      * @param backPressManager {@link BackPressManager} to handle back press gesture.
+     * @param tabGridDialogControllerSupplier {@link TabGridDialogMediator.DialogController}
+     *         supplier for lazy initialization on first use.
      */
     TabSwitcherMediator(Context context, ResetHandler resetHandler,
             PropertyModel containerViewModel, TabModelSelector tabModelSelector,
@@ -288,7 +291,9 @@
             PriceWelcomeMessageController priceWelcomeMessageController,
             MultiWindowModeStateDispatcher multiWindowModeStateDispatcher, @TabListMode int mode,
             @Nullable OneshotSupplier<IncognitoReauthController> incognitoReauthControllerSupplier,
-            @Nullable BackPressManager backPressManager) {
+            @Nullable BackPressManager backPressManager,
+            @Nullable OneshotSupplier<TabGridDialogMediator.DialogController>
+                    tabGridDialogControllerSupplier) {
         mResetHandler = resetHandler;
         mContainerViewModel = containerViewModel;
         mTabModelSelector = tabModelSelector;
@@ -321,8 +326,9 @@
                         mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter();
                 mContainerViewModel.set(IS_INCOGNITO, currentTabModelFilter.isIncognito());
                 notifyBackPressStateChangedInternal();
-                if (mTabGridDialogController != null) {
-                    mTabGridDialogController.hideDialog(false);
+                if (mTabGridDialogControllerSupplier != null
+                        && mTabGridDialogControllerSupplier.hasValue()) {
+                    mTabGridDialogControllerSupplier.get().hideDialog(false);
                 }
                 if (!mContainerViewModel.get(IS_VISIBLE)) return;
 
@@ -530,6 +536,14 @@
         };
         mMultiWindowModeStateDispatcher.addObserver(mMultiWindowModeObserver);
         notifyBackPressStateChangedInternal();
+
+        mTabGridDialogControllerSupplier = tabGridDialogControllerSupplier;
+        if (mTabGridDialogControllerSupplier != null) {
+            mTabGridDialogControllerSupplier.onAvailable((tabGridDialogController) -> {
+                tabGridDialogController.getHandleBackPressChangedSupplier().addObserver(
+                        mNotifyBackPressedCallback);
+            });
+        }
     }
 
     /**
@@ -558,17 +572,6 @@
         }
     }
 
-    /**
-     * Set the controller of the TabGridDialog so that it can be directly controlled.
-     * @param tabGridDialogController The handler of the Grid Dialog
-     */
-    void setTabGridDialogController(
-            TabGridDialogMediator.DialogController tabGridDialogController) {
-        mTabGridDialogController = tabGridDialogController;
-        mTabGridDialogController.getHandleBackPressChangedSupplier().addObserver(
-                mNotifyBackPressedCallback);
-    }
-
     @VisibleForTesting
     int getSoftCleanupDelayForTesting() {
         return getSoftCleanupDelay();
@@ -730,10 +733,11 @@
 
     @Override
     public void prepareHideTabSwitcherView() {
-        if (mTabGridDialogController != null) {
+        if (mTabGridDialogControllerSupplier != null
+                && mTabGridDialogControllerSupplier.hasValue()) {
             // Don't wait until switcher container view hides.
             // Hide dialog before GTS hides.
-            mTabGridDialogController.hideDialog(false);
+            mTabGridDialogControllerSupplier.get().hideDialog(false);
         }
     }
 
@@ -743,10 +747,11 @@
         setVisibility(false);
         mContainerViewModel.set(ANIMATE_VISIBILITY_CHANGES, true);
 
-        if (mTabGridDialogController != null) {
+        if (mTabGridDialogControllerSupplier != null
+                && mTabGridDialogControllerSupplier.hasValue()) {
             // Don't wait until didSelectTab(), which is after the GTS animation.
             // We need to hide the dialog immediately.
-            mTabGridDialogController.hideDialog(false);
+            mTabGridDialogControllerSupplier.get().hideDialog(false);
         }
     }
 
@@ -864,7 +869,8 @@
             return false;
         }
 
-        if (mTabGridDialogController != null && mTabGridDialogController.handleBackPressed()) {
+        if (mTabGridDialogControllerSupplier != null && mTabGridDialogControllerSupplier.hasValue()
+                && mTabGridDialogControllerSupplier.get().handleBackPressed()) {
             return true;
         }
 
@@ -897,7 +903,8 @@
             return true;
         }
 
-        if (mTabGridDialogController != null && mTabGridDialogController.isVisible()) {
+        if (mTabGridDialogControllerSupplier != null && mTabGridDialogControllerSupplier.hasValue()
+                && mTabGridDialogControllerSupplier.get().isVisible()) {
             return true;
         }
         return false;
@@ -1016,9 +1023,11 @@
                     mNotifyBackPressedCallback);
         }
 
-        if (mTabGridDialogController != null) {
-            mTabGridDialogController.getHandleBackPressChangedSupplier().removeObserver(
-                    mNotifyBackPressedCallback);
+        if (mTabGridDialogControllerSupplier != null
+                && mTabGridDialogControllerSupplier.hasValue()) {
+            mTabGridDialogControllerSupplier.get()
+                    .getHandleBackPressChangedSupplier()
+                    .removeObserver(mNotifyBackPressedCallback);
         }
 
         if (mIncognitoReauthController != null) {
@@ -1050,13 +1059,13 @@
     public TabListMediator.TabActionListener openTabGridDialog(Tab tab) {
         if (!ableToOpenDialog(tab)) return null;
         assert getRelatedTabs(tab.getId()).size() != 1;
-        assert mTabGridDialogController != null;
+        assert mTabGridDialogControllerSupplier != null;
         return tabId -> {
             List<Tab> relatedTabs = getRelatedTabs(tabId);
             if (relatedTabs.size() == 0) {
                 relatedTabs = null;
             }
-            mTabGridDialogController.resetWithListOfTabs(relatedTabs);
+            mTabGridDialogControllerSupplier.get().resetWithListOfTabs(relatedTabs);
             RecordUserAction.record("TabGridDialog.ExpandedFromSwitcher");
         };
     }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
index e20bf9c5f..480a93d4 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
@@ -45,6 +45,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
@@ -205,9 +206,12 @@
         TabGridDialogMediator.DialogController controller =
                 TabUiFeatureUtilities.isTabGroupsAndroidEnabled(mContext) ? mTabGridDialogController
                                                                           : null;
+        Supplier<TabGridDialogMediator.DialogController> controllerSupplier = () -> {
+            return controller;
+        };
         mTabGroupUiMediator = new TabGroupUiMediator(mContext, mVisibilityController, mResetHandler,
                 mModel, mTabModelSelector, mTabCreatorManager, mLayoutStateProviderSupplier,
-                mIncognitoStateProvider, controller, mOmniboxFocusStateSupplier);
+                mIncognitoStateProvider, controllerSupplier, mOmniboxFocusStateSupplier);
 
         if (currentTab == null) {
             verifyNeverReset();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index 0813a60..ee6b6f7 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -107,6 +108,8 @@
 
     private final OneshotSupplierImpl<IncognitoReauthController>
             mIncognitoReauthControllerSupplier = new OneshotSupplierImpl<>();
+    private final OneshotSupplierImpl<TabGridDialogMediator.DialogController>
+            mTabGridDialogControllerSupplier = new OneshotSupplierImpl<>();
 
     @Mock
     TabSwitcherMediator.ResetHandler mResetHandler;
@@ -161,6 +164,9 @@
     private ArgumentCaptor<IncognitoReauthManager.IncognitoReauthCallback>
             mIncognitoReauthCallbackArgumentCaptor;
 
+    @Mock
+    private TabSelectionEditorCoordinator.TabSelectionEditorController mEditorController;
+
     private Tab mTab1;
     private Tab mTab2;
     private Tab mTab3;
@@ -218,25 +224,25 @@
         doReturn(true)
                 .when(mMultiWindowModeStateDispatcher)
                 .addObserver(mMultiWindowModeObserverCaptor.capture());
-        TabSelectionEditorCoordinator.TabSelectionEditorController controller =
-                mock(TabSelectionEditorCoordinator.TabSelectionEditorController.class);
         doReturn(new ObservableSupplierImpl<Boolean>())
-                .when(controller)
+                .when(mEditorController)
                 .getHandleBackPressChangedSupplier();
         doReturn(new ObservableSupplierImpl<Boolean>())
                 .when(mTabGridDialogController)
                 .getHandleBackPressChangedSupplier();
         when(mIncognitoReauthController.isIncognitoReauthPending()).thenReturn(false);
         mIncognitoReauthControllerSupplier.set(mIncognitoReauthController);
+        mTabGridDialogControllerSupplier.set(mTabGridDialogController);
 
         mModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
         mModel.addObserver(mPropertyObserver);
         mMediator = new TabSwitcherMediator(mContext, mResetHandler, mModel, mTabModelSelector,
                 mBrowserControlsStateProvider, mCompositorViewHolder, null, mMessageItemsController,
                 mPriceWelcomeMessageController, mMultiWindowModeStateDispatcher,
-                TabListCoordinator.TabListMode.GRID, mIncognitoReauthControllerSupplier, null);
+                TabListCoordinator.TabListMode.GRID, mIncognitoReauthControllerSupplier, null,
+                mTabGridDialogControllerSupplier);
 
-        mMediator.initWithNative(controller, null);
+        mMediator.initWithNative(mEditorController, null);
         mMediator.addTabSwitcherViewObserver(mTabSwitcherViewObserver);
         mMediator.setOnTabSelectingListener(mLayout::onTabSelecting);
         verify(mIncognitoReauthController, times(1))
@@ -318,7 +324,6 @@
     @Test
     public void hidesWithAnimation() {
         initAndAssertAllProperties();
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         mMediator.showTabSwitcherView(true);
 
         assertThat(
@@ -337,7 +342,6 @@
     @Test
     public void hidesWithoutAnimation() {
         initAndAssertAllProperties();
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         mMediator.showTabSwitcherView(true);
 
         assertThat(
@@ -363,13 +367,22 @@
 
     @Test
     public void beforeHideTabSwitcherView_NullController() {
+        reset(mTabGridDialogController);
+        mMediator = new TabSwitcherMediator(mContext, mResetHandler, mModel, mTabModelSelector,
+                mBrowserControlsStateProvider, mCompositorViewHolder, null, mMessageItemsController,
+                mPriceWelcomeMessageController, mMultiWindowModeStateDispatcher,
+                TabListCoordinator.TabListMode.GRID, mIncognitoReauthControllerSupplier, null,
+                null);
+        mMediator.initWithNative(mEditorController, null);
+        mMediator.addTabSwitcherViewObserver(mTabSwitcherViewObserver);
+        mMediator.setOnTabSelectingListener(mLayout::onTabSelecting);
+
         mMediator.prepareHideTabSwitcherView();
         verifyNoMoreInteractions(mTabGridDialogController);
     }
 
     @Test
     public void beforeHideTabSwitcherView_WithController() {
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         mMediator.prepareHideTabSwitcherView();
         verify(mTabGridDialogController).hideDialog(eq(false));
     }
@@ -417,9 +430,6 @@
         initAndAssertAllProperties();
         mModel.set(TabListContainerProperties.IS_VISIBLE, true);
 
-        // Setup dialog reset handler. Default setup is that dialog handler is null.
-        mMediator.setTabGridDialogController(mTabGridDialogController);
-
         doReturn(true).when(mTabModelFilter).isIncognito();
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mTabModel, null);
         verify(mResetHandler).resetWithTabList(eq(mTabModelFilter), eq(false), eq(false));
@@ -432,6 +442,16 @@
 
     @Test
     public void resetsAfterNewTabModelSelected_DialogNotEnabled() {
+        reset(mTabGridDialogController);
+        mMediator = new TabSwitcherMediator(mContext, mResetHandler, mModel, mTabModelSelector,
+                mBrowserControlsStateProvider, mCompositorViewHolder, null, mMessageItemsController,
+                mPriceWelcomeMessageController, mMultiWindowModeStateDispatcher,
+                TabListCoordinator.TabListMode.GRID, mIncognitoReauthControllerSupplier, null,
+                null);
+        mMediator.initWithNative(mEditorController, null);
+        mMediator.addTabSwitcherViewObserver(mTabSwitcherViewObserver);
+        mMediator.setOnTabSelectingListener(mLayout::onTabSelecting);
+
         initAndAssertAllProperties();
         mModel.set(TabListContainerProperties.IS_VISIBLE, true);
 
@@ -450,9 +470,6 @@
         initAndAssertAllProperties();
         assertThat(mModel.get(TabListContainerProperties.IS_VISIBLE), equalTo(false));
 
-        // Setup dialog reset handler. Default setup is that dialog handler is null.
-        mMediator.setTabGridDialogController(mTabGridDialogController);
-
         doReturn(true).when(mTabModelFilter).isIncognito();
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mTabModel, null);
         verify(mResetHandler, never()).resetWithTabList(any(), anyBoolean(), anyBoolean());
@@ -725,7 +742,6 @@
     @Test
     @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
     public void openDialogButton_SingleTab() {
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         // Mock that tab 1 is a single tab.
         doReturn(new ArrayList<>(Arrays.asList(mTab1)))
                 .when(mTabModelFilter)
@@ -736,7 +752,6 @@
     @Test
     @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
     public void openDialogButton_TabGroup_NotEmpty() {
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         // Set up a tab group.
         Tab newTab = prepareTab(TAB4_ID, TAB4_TITLE);
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, newTab));
@@ -752,7 +767,6 @@
     @Test
     @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
     public void openDialogButton_TabGroup_Empty() {
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         // Assume that due to tab model change, current group becomes empty in current model.
         doReturn(new ArrayList<>()).when(mTabModelFilter).getRelatedTabList(TAB1_ID);
 
@@ -910,14 +924,16 @@
         new TabSwitcherMediator(mContext, mResetHandler, mModel, mTabModelSelector,
                 mBrowserControlsStateProvider, mCompositorViewHolder, null, mMessageItemsController,
                 mPriceWelcomeMessageController, mMultiWindowModeStateDispatcher,
-                TabListCoordinator.TabListMode.GRID, mIncognitoReauthControllerSupplier, null);
+                TabListCoordinator.TabListMode.GRID, mIncognitoReauthControllerSupplier, null,
+                mTabGridDialogControllerSupplier);
         assertEquals(16, mModel.get(TabListContainerProperties.BOTTOM_PADDING));
 
         mModel.set(TabListContainerProperties.BOTTOM_PADDING, 0);
         new TabSwitcherMediator(mContext, mResetHandler, mModel, mTabModelSelector,
                 mBrowserControlsStateProvider, mCompositorViewHolder, null, mMessageItemsController,
                 mPriceWelcomeMessageController, mMultiWindowModeStateDispatcher,
-                TabListCoordinator.TabListMode.STRIP, mIncognitoReauthControllerSupplier, null);
+                TabListCoordinator.TabListMode.STRIP, mIncognitoReauthControllerSupplier, null,
+                mTabGridDialogControllerSupplier);
         assertEquals(0, mModel.get(TabListContainerProperties.BOTTOM_PADDING));
     }
 
@@ -945,7 +961,6 @@
         initAndAssertAllProperties();
         Assert.assertFalse(mMediator.shouldInterceptBackPress());
 
-        mMediator.setTabGridDialogController(mTabGridDialogController);
         doReturn(true).when(mTabGridDialogController).isVisible();
         Assert.assertTrue("Should intercept back press if tab grid dialog is visible",
                 mMediator.shouldInterceptBackPress());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
index 7d569b1..9cb8772 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/creator/CreatorActivity.java
@@ -57,7 +57,6 @@
     public void onCreate(Bundle savedInstanceState) {
         byte[] mWebFeedId =
                 getIntent().getByteArrayExtra(CreatorIntentConstants.CREATOR_WEB_FEED_ID);
-        String mTitle = getIntent().getStringExtra(CreatorIntentConstants.CREATOR_TITLE);
         String mUrl = getIntent().getStringExtra(CreatorIntentConstants.CREATOR_URL);
         int mEntryPoint = getIntent().getIntExtra(
                 CreatorIntentConstants.CREATOR_ENTRY_POINT, SingleWebFeedEntryPoint.OTHER);
@@ -72,10 +71,9 @@
         super.onCreate(savedInstanceState);
         IntentRequestTracker intentRequestTracker = IntentRequestTracker.createFromActivity(this);
         mWindowAndroid = new ActivityWindowAndroid(this, false, intentRequestTracker);
-        CreatorCoordinator coordinator =
-                new CreatorCoordinator(this, mWebFeedId, getSnackbarManager(), mWindowAndroid,
-                        mProfile, mTitle, mUrl, this::createWebContents, this::createNewTab,
-                        mTabShareDelegateSupplier, mEntryPoint);
+        CreatorCoordinator coordinator = new CreatorCoordinator(this, mWebFeedId,
+                getSnackbarManager(), mWindowAndroid, mProfile, mUrl, this::createWebContents,
+                this::createNewTab, mTabShareDelegateSupplier, mEntryPoint);
 
         mBottomSheetController = coordinator.getBottomSheetController();
         ShareDelegate shareDelegate = new ShareDelegateImpl(mBottomSheetController,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/DEPS
index 4d74607..eaf525e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/DEPS
@@ -1,4 +1,4 @@
 include_rules = [
   "+chrome/browser/android/browserservices",
-  "+components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links",
+  "+components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification",
 ]
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
index 68e30e5..86a7b7b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
@@ -18,8 +18,8 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.content_public.browser.GlobalRenderFrameHostId;
 import org.chromium.content_public.browser.LifecycleState;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index de055cf..9230aa9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -22,6 +22,7 @@
 import android.view.PointerIcon;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
@@ -104,9 +105,11 @@
                    ChromeAccessibilityUtil.Observer, TabObscuringHandler.Observer,
                    ViewGroup.OnHierarchyChangeListener {
     private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500;
-    private static MutableFlagWithSafeDefault sDeferKeepScreenOnFlag =
+    private static final MutableFlagWithSafeDefault sDeferKeepScreenOnFlag =
             new MutableFlagWithSafeDefault(
                     ChromeFeatureList.DEFER_KEEP_SCREEN_ON_DURING_GESTURE, false);
+    private static final MutableFlagWithSafeDefault sDeferNotifyInMotion =
+            new MutableFlagWithSafeDefault(ChromeFeatureList.DEFER_NOTIFY_IN_MOTION, false);
     private Runnable mSetBackgroundRunnable;
 
     /**
@@ -754,7 +757,9 @@
             mInGesture = false;
             updateViewportSize();
         }
-        updateInMotion();
+        if (!sDeferNotifyInMotion.isEnabled()) {
+            updateInMotion();
+        }
     }
 
     private void updateInMotion() {
@@ -805,7 +810,16 @@
         updateLastActiveTouchEvent(e);
         updateIsInGesture(e);
         for (TouchEventObserver o : mTouchEventObservers) o.handleTouchEvent(e);
-        return super.dispatchTouchEvent(e);
+
+        // This is where input events go from android through native to the web content. This
+        // process is latency sensitive. Ideally observers that might be expensive, such as
+        // notifying in motion, should be done after this.
+        boolean handled = super.dispatchTouchEvent(e);
+
+        if (sDeferNotifyInMotion.isEnabled()) {
+            updateInMotion();
+        }
+        return handled;
     }
 
     private void updateLastActiveTouchEvent(MotionEvent e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStamp.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStamp.java
index 76848ed..844ef0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStamp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStamp.java
@@ -35,7 +35,6 @@
     private static final String RELATED_SEARCHES_EXPERIMENT_RECIPE_STAGE = "R";
     private static final String RELATED_SEARCHES_NO_EXPERIMENT = "n";
     private static final String RELATED_SEARCHES_LANGUAGE_RESTRICTION = "l";
-    private static final String RELATED_SEARCHES_DARK_LAUNCH = "d";
     private static final String RELATED_SEARCHES_USER_INTERACTION = "U";
     private static final String RELATED_SEARCHES_SELECTED_POSITION = "p";
     private static final String NO_EXPERIMENT_STAMP = RELATED_SEARCHES_STAMP_VERSION
@@ -45,17 +44,6 @@
     private boolean mDisableDefaultAllowedLanguagesForTesting;
 
     /**
-     * Verbosity param used to control requested results.
-     * <ul>
-     *   <li> "d" specifies a dark launch, which means return none </li>
-     *   <li> "v" for verbose  </li>
-     *   <li> "x" for extra verbose </li>
-     *   <li> "" for the default </li></ul>
-     * See also the verbosity entry in about_flags to correlate.
-     */
-    private static final String RELATED_SEARCHES_VERBOSITY_PARAM = "verbosity";
-
-    /**
      * Creates a Related Searches Stamp handling instance that works with the given {@code
      * ContextualSearchPolicy}
      */
@@ -208,27 +196,13 @@
     }
 
     /**
-     * Returns the number of results to request from the server, as a single coded letter, or
-     * {@code null} if the server should just return the default number of Related Searches.
-     */
-    private String getNumberOfRelatedSearchesToRequestCode() {
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.RELATED_SEARCHES_UI)) {
-            return RELATED_SEARCHES_DARK_LAUNCH;
-        }
-        // Return the Feature param, which could be an empty string if not present.
-        return ChromeFeatureList.getFieldTrialParamByFeature(
-                ChromeFeatureList.RELATED_SEARCHES_UI, RELATED_SEARCHES_VERBOSITY_PARAM);
-    }
-
-    /**
      * Builds the "stamp" that tracks the processing of Related Searches and describes what was
      * done at each stage using a shorthand notation. The notation is described in go/rsearches-dd
      * here: http://doc/1DryD8NAP5LQAo326LnxbqkIDCNfiCOB7ak3gAYaNWAM#bookmark=id.nx7ivu2upqw
      * <p>The first stage is built here: "1" for schema version one, "R" for the configuration
      * Recipe which has a character describing how we'll formulate the search. Typically all of
      * this comes from the Variations config at runtime. We programmatically append an "l" that
-     * indicates a language restriction (when present), and currently a "d" for "dark launch" so
-     * the server knows to return normal Contextual Search results for this older client.
+     * indicates a language restriction (when present).
      * @param isLanguageRestricted Whether there are any language restrictions needed by the
      *        server.
      * @return A string that represents and encoded description of the current request processing.
@@ -239,10 +213,7 @@
         if (TextUtils.isEmpty(experimentConfigStamp)) experimentConfigStamp = NO_EXPERIMENT_STAMP;
         StringBuilder stampBuilder = new StringBuilder().append(experimentConfigStamp);
         if (isLanguageRestricted) stampBuilder.append(RELATED_SEARCHES_LANGUAGE_RESTRICTION);
-        // Add a tag so the server knows this version of the client is doing a dark launch
-        // and cannot decode Related Searches, unless overridden by a Feature flag.
-        String resultsToReturnCode = getNumberOfRelatedSearchesToRequestCode();
-        if (resultsToReturnCode.length() > 0) stampBuilder.append(resultsToReturnCode);
+
         return stampBuilder.toString();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index a7c3ce6..4eeedf58 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -36,7 +36,7 @@
 import org.chromium.chrome.browser.browserservices.verification.ChromeOriginVerifier;
 import org.chromium.chrome.browser.browserservices.verification.ChromeOriginVerifierFactory;
 import org.chromium.chrome.browser.browserservices.verification.ChromeOriginVerifierFactoryImpl;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.installedapp.InstalledAppProviderImpl;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
index 8e32e79..1ed0ad4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
@@ -13,6 +13,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chrome.browser.metrics.ChangeMetricsReportingStateCalledFrom;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -70,7 +71,8 @@
      *         collect stats.
      */
     static void acceptTermsOfService(boolean allowMetricsAndCrashUploading) {
-        UmaSessionStats.changeMetricsReportingConsent(allowMetricsAndCrashUploading);
+        UmaSessionStats.changeMetricsReportingConsent(
+                allowMetricsAndCrashUploading, ChangeMetricsReportingStateCalledFrom.UI_FIRST_RUN);
         SharedPreferencesManager.getInstance().writeBoolean(
                 ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, true);
         setEulaAccepted();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
index 414ba33..346fecfed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
@@ -158,13 +158,14 @@
      * Updates the metrics services based on a change of consent. This can happen during first-run
      * flow, and when the user changes their preferences.
      */
-    public static void changeMetricsReportingConsent(boolean consent) {
+    public static void changeMetricsReportingConsent(
+            boolean consent, @ChangeMetricsReportingStateCalledFrom int calledFrom) {
         PrivacyPreferencesManagerImpl privacyManager = PrivacyPreferencesManagerImpl.getInstance();
         // Update the metrics reporting preference.
         privacyManager.setUsageAndCrashReporting(consent);
 
         // Perform native changes needed to reflect the new consent value.
-        UmaSessionStatsJni.get().changeMetricsReportingConsent(consent);
+        UmaSessionStatsJni.get().changeMetricsReportingConsent(consent, calledFrom);
 
         updateMetricsServiceState();
     }
@@ -271,7 +272,7 @@
     @NativeMethods
     interface Natives {
         long init();
-        void changeMetricsReportingConsent(boolean consent);
+        void changeMetricsReportingConsent(boolean consent, int calledFrom);
         void initMetricsAndCrashReportingForTesting();
         void unsetMetricsAndCrashReportingForTesting();
         void updateMetricsAndCrashReportingForTesting(boolean consent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java
index 2a772a1..5751848 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/GoogleServicesSettings.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.metrics.ChangeMetricsReportingStateCalledFrom;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
@@ -229,7 +230,8 @@
         } else if (PREF_SEARCH_SUGGESTIONS.equals(key)) {
             mPrefService.setBoolean(Pref.SEARCH_SUGGEST_ENABLED, (boolean) newValue);
         } else if (PREF_USAGE_AND_CRASH_REPORTING.equals(key)) {
-            UmaSessionStats.changeMetricsReportingConsent((boolean) newValue);
+            UmaSessionStats.changeMetricsReportingConsent(
+                    (boolean) newValue, ChangeMetricsReportingStateCalledFrom.UI_SETTINGS);
         } else if (PREF_URL_KEYED_ANONYMIZED_DATA.equals(key)) {
             UnifiedConsentServiceBridge.setUrlKeyedAnonymizedDataCollectionEnabled(
                     Profile.getLastUsedRegularProfile(), (boolean) newValue);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
index f8e57c3..9cc6737e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -334,25 +334,24 @@
      */
     protected static final ImmutableMap<String, Boolean> ENABLE_NONE = ImmutableMap.of(
             // All false
-            ChromeFeatureList.RELATED_SEARCHES, false, ChromeFeatureList.RELATED_SEARCHES_UI, false,
+            ChromeFeatureList.RELATED_SEARCHES, false,
             ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, false);
 
     /** This is the Related Searches Feature in the MVP configuration. */
     private static final ImmutableMap<String, Boolean> ENABLE_RELATED_SEARCHES = ImmutableMap.of(
             // Related Searches needs these 3:
-            ChromeFeatureList.RELATED_SEARCHES, true, ChromeFeatureList.RELATED_SEARCHES_UI, true,
+            ChromeFeatureList.RELATED_SEARCHES, true,
             ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, false);
 
     /** This is the helper-text Feature. */
-    private static final ImmutableMap<String, Boolean> ENABLE_FORCE_CAPTION = ImmutableMap.of(
-            ChromeFeatureList.RELATED_SEARCHES, false, ChromeFeatureList.RELATED_SEARCHES_UI, false,
-            // Just this one enabled:
-            ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, true);
+    private static final ImmutableMap<String, Boolean> ENABLE_FORCE_CAPTION =
+            ImmutableMap.of(ChromeFeatureList.RELATED_SEARCHES, false,
+                    // Just this one enabled:
+                    ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, true);
 
     /** This is the helper-text Feature with Related Searches */
     private static final ImmutableMap<String, Boolean> ENABLE_FORCE_CAPTION_WITH_RELATED_SEARCHES =
             ImmutableMap.of(ChromeFeatureList.RELATED_SEARCHES, true,
-                    ChromeFeatureList.RELATED_SEARCHES_UI, true,
                     ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, true);
 
     //--------------------------------------------------------------------------------------------
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
index bd9177a..31c3057 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
@@ -22,6 +22,7 @@
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.settings.MainSettings;
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
@@ -57,6 +58,8 @@
                                                 .around(mMainSettingsTestRule)
                                                 .around(mSearchEngineSettingsTestRule);
 
+    private TemplateUrlService mTemplateUrlService;
+
     /**
      * Change search engine and make sure it works correctly.
      */
@@ -81,18 +84,15 @@
             // Simulate selecting the third search engine, ensure that TemplateUrlService is
             // updated.
             String keyword2 = pref.setValueForTesting("2");
-            TemplateUrlService templateUrlService = TemplateUrlServiceFactory.get();
             Assert.assertEquals(
-                    keyword2, templateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
+                    keyword2, mTemplateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
 
             // Simulate selecting the fourth search engine.
             String keyword3 = pref.getKeywordFromIndexForTesting(3);
-            String url = templateUrlService.getSearchEngineUrlFromTemplateUrl(keyword3);
+            String url = mTemplateUrlService.getSearchEngineUrlFromTemplateUrl(keyword3);
             keyword3 = pref.setValueForTesting("3");
-            Assert.assertEquals(keyword3,
-                    TemplateUrlServiceFactory.get()
-                            .getDefaultSearchEngineTemplateUrl()
-                            .getKeyword());
+            Assert.assertEquals(
+                    keyword3, mTemplateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
         });
     }
 
@@ -105,7 +105,7 @@
                 () -> { ChromeBrowserInitializer.getInstance().handleSynchronousStartup(); });
 
         ensureTemplateUrlServiceLoaded();
-        CriteriaHelper.pollUiThread(() -> TemplateUrlServiceFactory.get().isDefaultSearchManaged());
+        CriteriaHelper.pollUiThread(() -> mTemplateUrlService.isDefaultSearchManaged());
 
         mMainSettingsTestRule.startSettingsActivity();
 
@@ -154,19 +154,17 @@
             int index = indexOfFirstHttpSearchEngine(pref);
             String keyword = pref.setValueForTesting(Integer.toString(index));
 
-            TemplateUrlService templateUrlService = TemplateUrlServiceFactory.get();
             Assert.assertEquals(
-                    keyword, templateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
+                    keyword, mTemplateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
         });
     }
 
     private int indexOfFirstHttpSearchEngine(SearchEngineSettings pref) {
-        TemplateUrlService templateUrlService = TemplateUrlServiceFactory.get();
-        List<TemplateUrl> urls = templateUrlService.getTemplateUrls();
+        List<TemplateUrl> urls = mTemplateUrlService.getTemplateUrls();
         int index;
         for (index = 0; index < urls.size(); ++index) {
             String keyword = pref.getKeywordFromIndexForTesting(index);
-            String url = templateUrlService.getSearchEngineUrlFromTemplateUrl(keyword);
+            String url = mTemplateUrlService.getSearchEngineUrlFromTemplateUrl(keyword);
             if (url.startsWith("http:")) {
                 return index;
             }
@@ -179,16 +177,20 @@
         // Make sure the template_url_service is loaded.
         final CallbackHelper onTemplateUrlServiceLoadedHelper = new CallbackHelper();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            if (TemplateUrlServiceFactory.get().isLoaded()) {
+            if (mTemplateUrlService == null) {
+                mTemplateUrlService = TemplateUrlServiceFactory.getForProfile(
+                        Profile.getLastUsedRegularProfile());
+            }
+            if (mTemplateUrlService.isLoaded()) {
                 onTemplateUrlServiceLoadedHelper.notifyCalled();
             } else {
-                TemplateUrlServiceFactory.get().registerLoadListener(new LoadListener() {
+                mTemplateUrlService.registerLoadListener(new LoadListener() {
                     @Override
                     public void onTemplateUrlServiceLoaded() {
                         onTemplateUrlServiceLoadedHelper.notifyCalled();
                     }
                 });
-                TemplateUrlServiceFactory.get().load();
+                mTemplateUrlService.load();
             }
         });
         onTemplateUrlServiceLoadedHelper.waitForCallback(0);
diff --git a/chrome/android/junit/DEPS b/chrome/android/junit/DEPS
index 38ec2fc..1e7a5c9 100644
--- a/chrome/android/junit/DEPS
+++ b/chrome/android/junit/DEPS
@@ -27,7 +27,7 @@
   "+components/browser_ui/test/android",
   "+components/browser_ui/widget/android",
   "+components/commerce/core",
-  "+components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links",
+  "+components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification",
   "+components/dom_distiller/core",
   "+components/feature_engagement/public/android",
   "+components/offline_items_collection/core/android/java/src",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java
index d5185835..320ad74 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java
@@ -36,7 +36,7 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.externalauth.ExternalAuthUtils;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java
index 6884cad..25081c5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.compositor;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -46,23 +47,40 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
 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.components.browser_ui.widget.TouchEventObserver;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.mojom.VirtualKeyboardMode;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Unit tests for {@link CompositorViewHolder}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class CompositorViewHolderUnitTest {
-    @Rule
-    public TestRule mProcessor = new Features.JUnitProcessor();
-
     // Since these tests don't depend on the heights being pixels, we can use these as dpi directly.
     private static final int TOOLBAR_HEIGHT = 56;
 
+    private static final long TOUCH_TIME = 0;
+    private static final MotionEvent MOTION_EVENT_DOWN =
+            MotionEvent.obtain(TOUCH_TIME, TOUCH_TIME, MotionEvent.ACTION_DOWN, 1, 1, 0);
+    private static final MotionEvent MOTION_EVENT_UP =
+            MotionEvent.obtain(TOUCH_TIME, TOUCH_TIME, MotionEvent.ACTION_UP, 1, 1, 0);
+
+    enum EventSource {
+        IN_MOTION,
+        TOUCH_EVENT_OBSERVER;
+    }
+
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
     @Mock
     private Activity mActivity;
     @Mock
@@ -116,6 +134,7 @@
 
         mContext = new ContextThemeWrapper(
                 ApplicationProvider.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
+
         mCompositorViewHolder = spy(new CompositorViewHolder(mContext));
         mCompositorViewHolder.setCompositorViewForTesting(mCompositorView);
         mCompositorViewHolder.setBrowserControlsManager(mBrowserControlsManager);
@@ -127,6 +146,25 @@
         when(mContainerView.getWindowToken()).thenReturn(windowToken);
     }
 
+    private List<EventSource> observeTouchAndMotionEvents() {
+        List<EventSource> eventSequence = new ArrayList<>();
+        mCompositorViewHolder.getInMotionSupplier().addObserver(
+                (inMotion) -> eventSequence.add(EventSource.IN_MOTION));
+        // This touch observer is used as a proxy for when ViewGroup#dispatchTouchEvent is called,
+        // which is when the touch is propagated to children.
+        mCompositorViewHolder.addTouchEventObserver(new TouchEventObserver() {
+            @Override
+            public boolean shouldInterceptTouchEvent(MotionEvent e) {
+                return false;
+            }
+            @Override
+            public void handleTouchEvent(MotionEvent e) {
+                eventSequence.add(EventSource.TOUCH_EVENT_OBSERVER);
+            }
+        });
+        return eventSequence;
+    }
+
     // controlsResizeView tests ---
     // For these tests, we will simulate the scrolls assuming we either completely show or hide (or
     // scroll until the min-height) the controls and don't leave at in-between positions. The reason
@@ -392,27 +430,45 @@
 
     @Test
     public void testInMotionSupplier() {
-        long time = System.currentTimeMillis();
-        MotionEvent down = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent up = MotionEvent.obtain(time, time, MotionEvent.ACTION_UP, 1, 1, 0);
-
-        mCompositorViewHolder.dispatchTouchEvent(down);
-        mCompositorViewHolder.onInterceptTouchEvent(down);
+        mCompositorViewHolder.dispatchTouchEvent(MOTION_EVENT_DOWN);
+        mCompositorViewHolder.onInterceptTouchEvent(MOTION_EVENT_DOWN);
         Assert.assertTrue(mCompositorViewHolder.getInMotionSupplier().get());
 
-        mCompositorViewHolder.dispatchTouchEvent(up);
-        mCompositorViewHolder.onInterceptTouchEvent(up);
+        mCompositorViewHolder.dispatchTouchEvent(MOTION_EVENT_UP);
+        mCompositorViewHolder.onInterceptTouchEvent(MOTION_EVENT_UP);
         Assert.assertFalse(mCompositorViewHolder.getInMotionSupplier().get());
 
-        mCompositorViewHolder.dispatchTouchEvent(down);
-        mCompositorViewHolder.onInterceptTouchEvent(down);
+        mCompositorViewHolder.dispatchTouchEvent(MOTION_EVENT_DOWN);
+        mCompositorViewHolder.onInterceptTouchEvent(MOTION_EVENT_DOWN);
         Assert.assertTrue(mCompositorViewHolder.getInMotionSupplier().get());
 
         // Simulate a child handling a scroll, where they call requestDisallowInterceptTouchEvent
         // and then we no longer get onInterceptTouchEvent. The dispatchTouchEvent alone should
         // still cause our motion status to correctly update.
         mCompositorViewHolder.requestDisallowInterceptTouchEvent(true);
-        mCompositorViewHolder.dispatchTouchEvent(up);
+        mCompositorViewHolder.dispatchTouchEvent(MOTION_EVENT_UP);
         Assert.assertFalse(mCompositorViewHolder.getInMotionSupplier().get());
     }
+
+    @Test
+    @DisableFeatures(ChromeFeatureList.DEFER_NOTIFY_IN_MOTION)
+    public void testInMotionOrdering_NoDefer() {
+        // With the 'defer in motion' experiment disabled, touch events are routed to android UI
+        // before being sent to native/web content.
+        List<EventSource> eventSequence = observeTouchAndMotionEvents();
+        mCompositorViewHolder.dispatchTouchEvent(MOTION_EVENT_DOWN);
+        assertEquals(Arrays.asList(EventSource.IN_MOTION, EventSource.TOUCH_EVENT_OBSERVER),
+                eventSequence);
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.DEFER_NOTIFY_IN_MOTION)
+    public void testInMotionOrdering_WithDefer() {
+        // With the 'defer in motion' experiment enabled, touch events are routed to android UI
+        // after being sent to native/web content.
+        List<EventSource> eventSequence = observeTouchAndMotionEvents();
+        mCompositorViewHolder.dispatchTouchEvent(MOTION_EVENT_DOWN);
+        assertEquals(Arrays.asList(EventSource.TOUCH_EVENT_OBSERVER, EventSource.IN_MOTION),
+                eventSequence);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStampTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStampTest.java
index 09e4bea..c2af0d7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStampTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesStampTest.java
@@ -15,8 +15,6 @@
 import android.net.Uri;
 import android.text.TextUtils;
 
-import androidx.annotation.Nullable;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,10 +49,6 @@
     private static final String RELATED_SEARCHES_DARK_LAUNCH = "d";
     private static final String RELATED_SEARCHES_USER_INTERACTION = "U";
     private static final String RELATED_SEARCHES_SELECTED_POSITION = "p";
-    private static final String RELATED_SEARCHES_VERBOSITY_PARAM = "verbosity";
-    private static final String RELATED_SEARCHES_VERBOSITY_DEFAULT = "";
-    private static final String RELATED_SEARCHES_VERBOSITY_VERBOSE = "v";
-    private static final String RELATED_SEARCHES_VERBOSITY_EXTREME = "x";
 
     /**
      * The stamps to use for various experiment configurations. Note that users still may need
@@ -87,7 +81,6 @@
      * Values to return from Shadows.
      * These must be static because the original and shadow methods are static.
      */
-    private static String sRelatedSearchesVerbosity;
     private static String sRelatedSearchesLanguageAllowlist;
     // These need to be Boolean instead of boolean so they can be static.
     private static Boolean sRelatedSearchesNeedsUrl;
@@ -105,14 +98,6 @@
      */
     @Implements(ChromeFeatureList.class)
     static class ShadowChromeFeatureList {
-        /** @see  {@link #setVerbosity} for how to define a return value for this function. */
-        @Implementation
-        protected static String getFieldTrialParamByFeature(String featureName, String paramName) {
-            assertThat(featureName, is(ChromeFeatureList.RELATED_SEARCHES_UI));
-            assertThat(paramName, is(RELATED_SEARCHES_VERBOSITY_PARAM));
-            return sRelatedSearchesVerbosity;
-        }
-
         @Implementation
         protected static boolean getFieldTrialParamByFeatureAsBoolean(
                 String featureName, String paramName, boolean defaultValue) {
@@ -187,18 +172,12 @@
 
     /** Resets all of the static return values for all our shadow classes. */
     private void resetShadows() {
-        sRelatedSearchesVerbosity = "";
         sRelatedSearchesLanguageAllowlist = "";
         sRelatedSearchesNeedsUrl = null;
         sRelatedSearchesNeedsContent = null;
         sRelatedSearchesExperimentConfigurationStamp = null;
     }
 
-    /** Sets the verbosity character that our shadow should return (normally set in the config). */
-    private void setVerbosity(String verbosityCharacter) {
-        sRelatedSearchesVerbosity = verbosityCharacter;
-    }
-
     /** Sets whether the user has allowed sending content (has done the opt-in). */
     private void setCanSendContent(boolean canSend) {
         mPolicy.overrideDecidedStateForTesting(canSend);
@@ -259,15 +238,6 @@
     }
 
     /**
-     * Sets a standard config setup for a particular Related Searches experiment arm.
-     * @param stampFromConfig The base stamp just as we expect it to be set in the experiment
-     *         config.
-     */
-    private void setStandardExperimentConfiguration(String stampFromConfig) {
-        setStandardExperimentConfiguration(stampFromConfig, RELATED_SEARCHES_VERBOSITY_DEFAULT);
-    }
-
-    /**
      * Sets a standard config setup for the default Related Searches launch configuration.
      */
     private void setStandardDefaultLaunchConfiguration() {
@@ -280,7 +250,7 @@
      *         config.
      */
     private void setStandardLaunchConfiguration(String stampFromConfig) {
-        setStandardExperimentConfiguration(stampFromConfig, RELATED_SEARCHES_VERBOSITY_DEFAULT);
+        setStandardExperimentConfiguration(stampFromConfig);
         clearLanguageAllowlist();
     }
 
@@ -288,14 +258,11 @@
      * Sets a standard config setup for a particular Related Searches experiment arm.
      * @param stampFromConfig The base stamp just as we expect it to be set in the experiment
      *         config.
-     * @param verbosity The verbosity param as we expect it to be set in the experiment config.
      */
-    private void setStandardExperimentConfiguration(
-            String stampFromConfig, @Nullable String verbosity) {
+    private void setStandardExperimentConfiguration(String stampFromConfig) {
         setStandardExperimentRequirements();
         setCanSendUrl(true);
         setCanSendContent(true);
-        setVerbosity(verbosity);
         setRelatedSearchesExperimentConfigurationStamp(stampFromConfig);
     }
 
@@ -469,38 +436,6 @@
 
     @Test
     @Feature({"RelatedSearches", "RelatedSearchesStamp"})
-    public void testGetVerbosityVerbose() {
-        setStandardDefaultLaunchConfiguration();
-        setVerbosity(RELATED_SEARCHES_VERBOSITY_VERBOSE);
-        assertThat("The verbose variant is not working as expected for the default launch "
-                        + "configuration!",
-                mStamp.getRelatedSearchesStamp(GERMAN),
-                is(EXPECTED_DEFAULT_STAMP + RELATED_SEARCHES_VERBOSITY_VERBOSE));
-    }
-
-    @Test
-    @Feature({"RelatedSearches", "RelatedSearchesStamp"})
-    public void testGetVerbosityExtreme() {
-        setStandardDefaultLaunchConfiguration();
-        setVerbosity(RELATED_SEARCHES_VERBOSITY_EXTREME);
-        assertThat("The verbose-extreme variant is not working as expected for the default launch "
-                        + "configuration!",
-                mStamp.getRelatedSearchesStamp(GERMAN),
-                is(EXPECTED_DEFAULT_STAMP + RELATED_SEARCHES_VERBOSITY_EXTREME));
-    }
-
-    @Test
-    @Feature({"RelatedSearches", "RelatedSearchesStamp"})
-    public void testGetVerbosityDefault() {
-        setStandardDefaultLaunchConfiguration();
-        setVerbosity("");
-        assertThat("The default setting for verbosity is not working as expected for the default "
-                        + "launch configuration!",
-                mStamp.getRelatedSearchesStamp(GERMAN), is(EXPECTED_DEFAULT_STAMP));
-    }
-
-    @Test
-    @Feature({"RelatedSearches", "RelatedSearchesStamp"})
     public void testUpdateUriForSelectedPosition() {
         setStandardDefaultLaunchConfiguration();
         Uri updatedUri = RelatedSearchesStamp.updateUriForSuggestionPosition(SAMPLE_URI, 3);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index 87020d8c..2098ca3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -48,9 +48,9 @@
 import org.chromium.chrome.browser.browserservices.verification.ChromeOriginVerifierJni;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifierJni;
-import org.chromium.components.digital_asset_links.OriginVerifierUnitTestSupport;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifierJni;
+import org.chromium.components.content_relationship_verification.OriginVerifierUnitTestSupport;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.embedder_support.util.ShadowUrlUtilities;
 
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 35b5b20..c43f3bfd 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-113.0.5619.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-113.0.5620.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 441c319..c9acdb09 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -1412,7 +1412,7 @@
 #if BUILDFLAG(IS_WIN)
   // TODO(zturner): Throbber icons and cursors are still stored in chrome.dll,
   // this can be killed once those are merged into resources.pak. See
-  // GlassBrowserFrameView::InitThrobberIcons(), https://crbug.com/368327 and
+  // BrowserFrameViewWin::InitThrobberIcons(), https://crbug.com/368327 and
   // https://crbug.com/1178117.
   ui::SetResourcesDataDLL(_AtlBaseModule.GetResourceInstance());
 #endif
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 28d5730..68c31f6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2096,6 +2096,7 @@
     "//components/component_updater/installer_policies",
     "//components/consent_auditor",
     "//components/content_capture/browser",
+    "//components/content_relationship_verification",
     "//components/content_settings/browser",
     "//components/content_settings/common:mojom",
     "//components/content_settings/core/browser",
@@ -2109,7 +2110,6 @@
     "//components/custom_handlers",
     "//components/device_event_log",
     "//components/device_reauth",
-    "//components/digital_asset_links",
     "//components/dom_distiller/content/browser",
     "//components/dom_distiller/content/common/mojom",
     "//components/domain_reliability",
@@ -5043,6 +5043,7 @@
       "//ash/webui/personalization_app/search:mojo_bindings",
       "//ash/webui/print_management",
       "//ash/webui/projector_app",
+      "//ash/webui/projector_app/mojom:annotator_mojo_bindings",
       "//ash/webui/scanning",
       "//ash/webui/scanning/mojom",
       "//ash/webui/shimless_rma",
@@ -5368,6 +5369,8 @@
       "lacros/launcher_search/search_controller_lacros.h",
       "lacros/metrics_reporting_observer.cc",
       "lacros/metrics_reporting_observer.h",
+      "lacros/multitask_menu_nudge_delegate_lacros.cc",
+      "lacros/multitask_menu_nudge_delegate_lacros.h",
       "lacros/net/lacros_extension_proxy_tracker.cc",
       "lacros/net/lacros_extension_proxy_tracker.h",
       "lacros/net/network_change_manager_bridge.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 16d4a533..a0785aa 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -117,7 +117,7 @@
   "+components/device_event_log",
   "+components/device_signals/core/browser",
   "+components/device_signals/core/common",
-  "+components/digital_asset_links",
+  "+components/content_relationship_verification",
   "+components/digital_goods",
   "+components/domain_reliability",
   "+components/dom_distiller/content/browser",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index aedc919b..a46f136c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1956,12 +1956,6 @@
     {"from URL", &kRelatedSearchesUrl, 1, nullptr},
     {"from content", &kRelatedSearchesContent, 1, nullptr},
 };
-const FeatureEntry::FeatureParam kRelatedSearchesUiVerbose = {"verbosity", "v"};
-const FeatureEntry::FeatureParam kRelatedSearchesUiExtreme = {"verbosity", "x"};
-const FeatureEntry::FeatureVariation kRelatedSearchesUiVariations[] = {
-    {"verbose", &kRelatedSearchesUiVerbose, 1, nullptr},
-    {"extreme", &kRelatedSearchesUiExtreme, 1, nullptr},
-};
 
 const FeatureEntry::FeatureParam kContextualSearchSuppressShortViewWith300Dp[] =
     {{"contextual_search_minimum_page_height_dp", "300"}};
@@ -3340,11 +3334,6 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kRelatedSearches,
                                     kRelatedSearchesVariations,
                                     "RelatedSearches")},
-    {"related-searches-ui", flag_descriptions::kRelatedSearchesUiName,
-     flag_descriptions::kRelatedSearchesUiDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kRelatedSearchesUi,
-                                    kRelatedSearchesUiVariations,
-                                    "RelatedSearchesUi")},
 #endif  // BUILDFLAG(IS_ANDROID)
     {"show-autofill-type-predictions",
      flag_descriptions::kShowAutofillTypePredictionsName,
@@ -3503,6 +3492,10 @@
      flag_descriptions::kCaptureModeDemoToolsName,
      flag_descriptions::kCaptureModeDemoToolsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kCaptureModeDemoTools)},
+    {"ash-capture-mode-gif-recording",
+     flag_descriptions::kCaptureModeGifRecordingName,
+     flag_descriptions::kCaptureModeGifRecordingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kGifRecording)},
     {"ash-overview-button", flag_descriptions::kOverviewButtonName,
      flag_descriptions::kOverviewButtonDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kOverviewButton)},
@@ -6969,12 +6962,6 @@
      kOsAll,
      FEATURE_VALUE_TYPE(features::kEnableBackForwardCacheForScreenReader)},
 
-#if !BUILDFLAG(IS_ANDROID)
-    {"closed-tab-cache", flag_descriptions::kClosedTabCacheName,
-     flag_descriptions::kClosedTabCacheDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kClosedTabCache)},
-#endif  // !BUILDFLAG(IS_ANDROID)
-
     {"windows-scrolling-personality",
      flag_descriptions::kWindowsScrollingPersonalityName,
      flag_descriptions::kWindowsScrollingPersonalityDescription, kOsAll,
@@ -7128,10 +7115,6 @@
      flag_descriptions::kFullUserAgentDescription, kOsDesktop | kOsAndroid,
      FEATURE_VALUE_TYPE(blink::features::kFullUserAgent)},
 
-    {"reduce-user-agent", flag_descriptions::kReduceUserAgentName,
-     flag_descriptions::kReduceUserAgentDescription, kOsDesktop | kOsAndroid,
-     FEATURE_VALUE_TYPE(blink::features::kReduceUserAgent)},
-
 #if BUILDFLAG(IS_WIN)
     {"run-video-capture-service-in-browser",
      flag_descriptions::kRunVideoCaptureServiceInBrowserProcessName,
@@ -8504,13 +8487,6 @@
      FEATURE_VALUE_TYPE(blink::features::kOriginAgentClusterDefaultEnabled)},
 
 #if !BUILDFLAG(IS_ANDROID)
-    {"enable-user-cloud-signin-restriction-policy",
-     flag_descriptions::kEnableUserCloudSigninRestrictionPolicyName,
-     flag_descriptions::kEnableUserCloudSigninRestrictionPolicyDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(
-         policy::features::kEnableUserCloudSigninRestrictionPolicyFetcher)},
-
     {"enable-code-based-rbd", flag_descriptions::kCodeBasedRBDName,
      flag_descriptions::kCodeBasedRBDDescription, kOsDesktop,
      FEATURE_WITH_PARAMS_VALUE_TYPE(commerce::kCodeBasedRBD,
@@ -8577,16 +8553,6 @@
      flag_descriptions::kReduceAcceptLanguageDescription, kOsAll,
      FEATURE_VALUE_TYPE(network::features::kReduceAcceptLanguage)},
 
-    {"reduce-user-agent-minor-version",
-     flag_descriptions::kReduceUserAgentMinorVersionName,
-     flag_descriptions::kReduceUserAgentMinorVersionDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kReduceUserAgentMinorVersion)},
-
-    {"reduce-user-agent-platform-oscpu",
-     flag_descriptions::kReduceUserAgentPlatformOsCpuName,
-     flag_descriptions::kReduceUserAgentPlatformOsCpuDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kReduceUserAgentPlatformOsCpu)},
-
 #if BUILDFLAG(IS_ANDROID)
     {"reduce-user-agent-android-version-device-model",
      flag_descriptions::kReduceUserAgentAndroidVersionDeviceModelName,
@@ -8671,6 +8637,10 @@
      flag_descriptions::kAmbientModeThrottleAnimationName,
      flag_descriptions::kAmbientModeThrottleAnimationDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kAmbientModeThrottleAnimation)},
+    {"ambient-mode-managed-screensaver-enabled",
+     flag_descriptions::kAmbientModeManagedScreensaverName,
+     flag_descriptions::kAmbientModeManagedScreensaverDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kAmbientModeManagedScreensaver)},
     {"enable-touchpads-in-diagnostics-app",
      flag_descriptions::kEnableTouchpadsInDiagnosticsAppName,
      flag_descriptions::kEnableTouchpadsInDiagnosticsAppDescription, kOsCrOS,
@@ -9148,7 +9118,7 @@
     {"chrome-root-store-enabled",
      flag_descriptions::kChromeRootStoreEnabledName,
      flag_descriptions::kChromeRootStoreEnabledDescription,
-     kOsWin | kOsMac | kOsAndroid,
+     kOsWin | kOsMac | kOsAndroid | kOsLinux | kOsCrOS | kOsLacros,
      FEATURE_VALUE_TYPE(net::features::kChromeRootStoreUsed)},
 #endif  // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 
diff --git a/chrome/browser/android/browserservices/verification/BUILD.gn b/chrome/browser/android/browserservices/verification/BUILD.gn
index 3a061cc..ae59c48 100644
--- a/chrome/browser/android/browserservices/verification/BUILD.gn
+++ b/chrome/browser/android/browserservices/verification/BUILD.gn
@@ -19,8 +19,8 @@
     "//chrome/browser/flags:java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
-    "//components/digital_asset_links:java",
-    "//components/digital_asset_links/android:java",
+    "//components/content_relationship_verification:java",
+    "//components/content_relationship_verification/android:java",
     "//components/embedder_support/android:util_java",
     "//components/externalauth/android:java",
     "//content/public/android:content_java",
@@ -46,7 +46,7 @@
     "//base:base_java_test_support",
     "//chrome/browser/flags:java",
     "//chrome/test/android:chrome_java_integration_test_support",
-    "//components/digital_asset_links/android:java",
+    "//components/content_relationship_verification/android:java",
     "//components/embedder_support/android:util_java",
     "//components/externalauth/android:java",
     "//content/public/android:content_java",
@@ -65,9 +65,9 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//chrome/browser/profiles/android:java",
-    "//components/digital_asset_links:java",
-    "//components/digital_asset_links/android:java",
-    "//components/digital_asset_links/android:junit_test_support",
+    "//components/content_relationship_verification:java",
+    "//components/content_relationship_verification/android:java",
+    "//components/content_relationship_verification/android:junit_test_support",
     "//components/embedder_support/android:util_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
diff --git a/chrome/browser/android/browserservices/verification/DEPS b/chrome/browser/android/browserservices/verification/DEPS
index 5fe5af7..0fff097 100644
--- a/chrome/browser/android/browserservices/verification/DEPS
+++ b/chrome/browser/android/browserservices/verification/DEPS
@@ -5,7 +5,7 @@
   "+chrome/browser/android/browserservices/metrics",
   "+chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences",
   "+chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles",
-  "+components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links",
+  "+components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification",
   "+components/embedder_support/android/java/src/org/chromium/components/embedder_support/util",
   "+components/externalauth/android/java/src/org/chromium/components/externalauth",
   "+content/public/android/java/src/org/chromium/content_public/browser",
diff --git a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifier.java b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifier.java
index 2fd8400..ce6b08e6 100644
--- a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifier.java
+++ b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifier.java
@@ -28,8 +28,8 @@
 import org.chromium.chrome.browser.browserservices.metrics.OriginVerifierMetricsRecorder.VerificationResult;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.Relationship;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.Relationship;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.externalauth.ExternalAuthUtils;
 import org.chromium.content_public.browser.BrowserContextHandle;
diff --git a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java
index e6638055..8844b87 100644
--- a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java
+++ b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java
@@ -27,11 +27,11 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
-import org.chromium.components.digital_asset_links.OriginVerifierJni;
-import org.chromium.components.digital_asset_links.OriginVerifierUnitTestSupport;
-import org.chromium.components.digital_asset_links.RelationshipCheckResult;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifierJni;
+import org.chromium.components.content_relationship_verification.OriginVerifierUnitTestSupport;
+import org.chromium.components.content_relationship_verification.RelationshipCheckResult;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierTest.java b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierTest.java
index cedd152..60ec230 100644
--- a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierTest.java
+++ b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierTest.java
@@ -22,7 +22,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.externalauth.ExternalAuthUtils;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
diff --git a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeVerificationResultStore.java b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeVerificationResultStore.java
index 13bd1cf3..21fdf47 100644
--- a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeVerificationResultStore.java
+++ b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeVerificationResultStore.java
@@ -9,7 +9,7 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.components.digital_asset_links.VerificationResultStore;
+import org.chromium.components.content_relationship_verification.VerificationResultStore;
 
 import java.util.HashSet;
 import java.util.Set;
diff --git a/chrome/browser/android/customtabs/chrome_origin_verifier.cc b/chrome/browser/android/customtabs/chrome_origin_verifier.cc
index a723ac9..174efab 100644
--- a/chrome/browser/android/customtabs/chrome_origin_verifier.cc
+++ b/chrome/browser/android/customtabs/chrome_origin_verifier.cc
@@ -7,7 +7,7 @@
 #include "base/android/jni_android.h"
 #include "chrome/browser/android/browserservices/verification/jni_headers/ChromeOriginVerifier_jni.h"
 #include "chrome/browser/browser_process.h"
-#include "components/digital_asset_links/origin_verifier.h"
+#include "components/content_relationship_verification/origin_verifier.h"
 
 namespace customtabs {
 
diff --git a/chrome/browser/android/metrics/uma_session_stats.cc b/chrome/browser/android/metrics/uma_session_stats.cc
index 388b2527..30ba52a 100644
--- a/chrome/browser/android/metrics/uma_session_stats.cc
+++ b/chrome/browser/android/metrics/uma_session_stats.cc
@@ -206,8 +206,10 @@
 // the Java side.
 static void JNI_UmaSessionStats_ChangeMetricsReportingConsent(
     JNIEnv*,
-    jboolean consent) {
-  UpdateMetricsPrefsOnPermissionChange(consent);
+    jboolean consent,
+    jint called_from) {
+  UpdateMetricsPrefsOnPermissionChange(
+      consent, static_cast<ChangeMetricsReportingStateCalledFrom>(called_from));
 
   // This function ensures a consent file in the data directory is either
   // created, or deleted, depending on consent. Starting up metrics services
diff --git a/chrome/browser/ash/browser_context_keyed_service_factories.cc b/chrome/browser/ash/browser_context_keyed_service_factories.cc
index 62e0ad03..aee7b7c7 100644
--- a/chrome/browser/ash/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/ash/browser_context_keyed_service_factories.cc
@@ -94,6 +94,7 @@
 #include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h"
 #include "chrome/browser/ui/ash/calendar/calendar_keyed_service_factory.h"
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/global_media_controls/cast_media_notification_producer_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
 #include "chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h"
@@ -206,6 +207,7 @@
   SyncedPrintersManagerFactory::GetInstance();
   tether::TetherServiceFactory::GetInstance();
   TtsEngineExtensionObserverChromeOS::EnsureFactoryBuilt();
+  GlanceablesKeyedServiceFactory::GetInstance();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/crosapi/browser_action.cc b/chrome/browser/ash/crosapi/browser_action.cc
index 40f05f7b..92cb4b6e 100644
--- a/chrome/browser/ash/crosapi/browser_action.cc
+++ b/chrome/browser/ash/crosapi/browser_action.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ash/crosapi/desk_template_ash.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profiles_state.h"
 #include "components/user_manager/user_manager.h"
 
 namespace crosapi {
@@ -393,16 +394,18 @@
 }
 
 // No window will be opened in the following circumstances:
-// 1. Lacros-chrome is initialized in the web Kiosk session
+// 1. Lacros-chrome is initialized in the Kiosk session
 // 2. Full restore is responsible for restoring/launching Lacros.
 // static
 std::unique_ptr<BrowserAction> BrowserAction::GetActionForSessionStart() {
-  if (user_manager::UserManager::Get()->IsLoggedInAsGuest())
+  if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
     return std::make_unique<NewWindowAction>(
         /*incognito=*/false, /*should_trigger_session_restore=*/false, -1);
-  if (user_manager::UserManager::Get()->IsLoggedInAsWebKioskApp() ||
-      ash::full_restore::MaybeCreateFullRestoreServiceForLacros())
+  }
+  if (profiles::IsKioskSession() ||
+      ash::full_restore::MaybeCreateFullRestoreServiceForLacros()) {
     return std::make_unique<NoOpAction>();
+  }
   return std::make_unique<NewWindowAction>(
       /*incognito=*/false, /*should_trigger_session_restore=*/true, -1);
 }
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index 6720dc6..ff35b74 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -262,16 +262,6 @@
     return false;
   }
 
-  // Profile migration should only be run for LacrosOnly i.e. no migration
-  // should run if Ash is still used as a browser.
-  if (IsAshWebBrowserEnabledForMigration(user, policy_init_state)) {
-    // TODO(crbug.com/1277848): Once `BrowserDataMigrator` stabilises, remove
-    // this log message.
-    LOG(WARNING)
-        << "Profile migration is only avaiable if Lacros is the only browser.";
-    return false;
-  }
-
   int attempts = GetMigrationAttemptCountForUser(local_state, user_id_hash);
   // TODO(crbug.com/1178702): Once BrowserDataMigrator stabilises, reduce the
   // log level to VLOG(1).
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
index 29bf6a0..722e12c4 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
@@ -122,6 +122,66 @@
   LoginManagerMixin login_manager_mixin_{&mixin_host_, {regular_user_}};
 };
 
+class BrowserDataMigratorCopyMigrateOnSignIn
+    : public BrowserDataMigratorOnSignIn {
+ public:
+  BrowserDataMigratorCopyMigrateOnSignIn() = default;
+  BrowserDataMigratorCopyMigrateOnSignIn(
+      BrowserDataMigratorCopyMigrateOnSignIn&) = delete;
+  BrowserDataMigratorCopyMigrateOnSignIn& operator=(
+      BrowserDataMigratorCopyMigrateOnSignIn&) = delete;
+  ~BrowserDataMigratorCopyMigrateOnSignIn() override = default;
+
+  void SetUp() override {
+    feature_list_.InitWithFeatures({ash::features::kLacrosSupport}, {});
+
+    BrowserDataMigratorOnSignIn::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Check that migration is triggered from signin flow if Lacros is enabled.
+IN_PROC_BROWSER_TEST_F(BrowserDataMigratorCopyMigrateOnSignIn,
+                       MigrateOnSignIn) {
+  base::RunLoop run_loop;
+  ScopedRestartAttemptForTesting scoped_restart_attempt(
+      base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
+  ASSERT_TRUE(LoginAsExistingRegularUser());
+  run_loop.Run();
+  EXPECT_TRUE(
+      FakeSessionManagerClient::Get()->request_browser_data_migration_called());
+  // Migration should be triggered in copy mode and not move mode.
+  EXPECT_TRUE(FakeSessionManagerClient::Get()
+                  ->request_browser_data_migration_mode_called());
+  EXPECT_EQ(FakeSessionManagerClient::Get()
+                ->request_browser_data_migration_mode_value(),
+            "copy");
+}
+
+// Check that migration marked as completed for a new user and thus migration is
+// not triggered from signin flow.
+IN_PROC_BROWSER_TEST_F(BrowserDataMigratorCopyMigrateOnSignIn,
+                       SkipMigrateOnSignInForNewUser) {
+  ash::test::ProfilePreparedWaiter profile_prepared(regular_user_.account_id);
+  ASSERT_TRUE(LoginAsRegularUser());
+  // Note that `ProfilePreparedWaiter` waits for
+  // `ExistingUserController::OnProfilePrepared()` to be called and this is
+  // called after `UserSessionManager::InitializeUserSession()` is called, which
+  // leads to `BrowserDataMigratorImpl::MaybeRestartToMigrate()`. Therefore by
+  // the time the wait ends, migration check would have happened.
+  profile_prepared.Wait();
+  EXPECT_FALSE(
+      FakeSessionManagerClient::Get()->request_browser_data_migration_called());
+  const std::string user_id_hash =
+      user_manager::FakeUserManager::GetFakeUsernameHash(
+          regular_user_.account_id);
+  EXPECT_TRUE(
+      crosapi::browser_util::IsCopyOrMoveProfileMigrationCompletedForUser(
+          g_browser_process->local_state(), user_id_hash));
+}
+
 class BrowserDataMigratorMoveMigrateOnSignInByPolicy
     : public BrowserDataMigratorOnSignIn {
  public:
@@ -193,29 +253,6 @@
             "move");
 }
 
-// Check that migration marked as completed for a new user and thus migration is
-// not triggered from signin flow. The key of this test is to use
-// `LoginAsRegularUser()` instead of `LoginAsExistingUser()`.
-IN_PROC_BROWSER_TEST_F(BrowserDataMigratorMoveMigrateOnSignInByFeature,
-                       SkipMigrateOnSignInForNewUser) {
-  ash::test::ProfilePreparedWaiter profile_prepared(regular_user_.account_id);
-  ASSERT_TRUE(LoginAsRegularUser());
-  // Note that `ProfilePreparedWaiter` waits for
-  // `ExistingUserController::OnProfilePrepared()` to be called and this is
-  // called after `UserSessionManager::InitializeUserSession()` is called, which
-  // leads to `BrowserDataMigratorImpl::MaybeRestartToMigrate()`. Therefore by
-  // the time the wait ends, migration check would have happened.
-  profile_prepared.Wait();
-  EXPECT_FALSE(
-      FakeSessionManagerClient::Get()->request_browser_data_migration_called());
-  const std::string user_id_hash =
-      user_manager::FakeUserManager::GetFakeUsernameHash(
-          regular_user_.account_id);
-  EXPECT_TRUE(
-      crosapi::browser_util::IsCopyOrMoveProfileMigrationCompletedForUser(
-          g_browser_process->local_state(), user_id_hash));
-}
-
 class BrowserDataMigratorResumeOnSignIn : public BrowserDataMigratorOnSignIn,
                                           public LocalStateMixin::Delegate {
  public:
@@ -422,10 +459,7 @@
   ~BrowserDataMigratorForKiosk() override = default;
 
   void SetUp() override {
-    feature_list_.InitWithFeatures(
-        {ash::features::kLacrosSupport, ash::features::kLacrosPrimary,
-         ash::features::kLacrosOnly},
-        {});
+    feature_list_.InitWithFeatures({ash::features::kLacrosSupport}, {});
 
     KioskBaseTest::SetUp();
   }
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc
index dbe93471..4debf01 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc
@@ -428,28 +428,12 @@
   }
 
   {
-    // If Lacros enabled, but is not Lacros only, migration should not run.
+    // If Lacros is enabled, migration should run.
     base::test::ScopedFeatureList feature_list;
-    feature_list.InitWithFeatures(
-        {ash::features::kLacrosSupport, ash::features::kLacrosPrimary}, {});
+    feature_list.InitWithFeatures({ash::features::kLacrosSupport}, {});
     EXPECT_EQ(crosapi::browser_util::GetMigrationMode(
                   user, crosapi::browser_util::PolicyInitState::kAfterInit),
               crosapi::browser_util::MigrationMode::kCopy);
-    EXPECT_FALSE(BrowserDataMigratorImpl::MaybeRestartToMigrateInternal(
-        user->GetAccountId(), user->username_hash(),
-        crosapi::browser_util::PolicyInitState::kAfterInit));
-  }
-
-  {
-    // If LacrosOnly is enabled, migration should run.
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitWithFeatures(
-        {ash::features::kLacrosSupport, ash::features::kLacrosPrimary,
-         ash::features::kLacrosOnly},
-        {});
-    EXPECT_EQ(crosapi::browser_util::GetMigrationMode(
-                  user, crosapi::browser_util::PolicyInitState::kAfterInit),
-              crosapi::browser_util::MigrationMode::kMove);
     EXPECT_TRUE(BrowserDataMigratorImpl::MaybeRestartToMigrateInternal(
         user->GetAccountId(), user->username_hash(),
         crosapi::browser_util::PolicyInitState::kAfterInit));
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 2b14823..d45963b 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -1538,14 +1538,19 @@
     case user_manager::USER_TYPE_KIOSK_APP:
     case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
     case user_manager::USER_TYPE_WEB_KIOSK_APP: {
-      policy::DeviceLocalAccountPolicyBroker* broker =
+      policy::DeviceLocalAccountPolicyService* policy_service =
           g_browser_process->platform_part()
               ->browser_policy_connector_ash()
-              ->GetDeviceLocalAccountPolicyService()
-              ->GetBrokerForUser(user->GetAccountId().GetUserEmail());
-      if (broker) {
-        core = broker->core();
-        component_policy_service = broker->component_policy_service();
+              ->GetDeviceLocalAccountPolicyService();
+      // `policy_service` can be nullptr, e.g. in unit tests.
+      if (policy_service) {
+        policy::DeviceLocalAccountPolicyBroker* broker =
+            policy_service->GetBrokerForUser(
+                user->GetAccountId().GetUserEmail());
+        if (broker) {
+          core = broker->core();
+          component_policy_service = broker->component_policy_service();
+        }
       }
       break;
     }
diff --git a/chrome/browser/ash/crosapi/browser_manager_unittest.cc b/chrome/browser/ash/crosapi/browser_manager_unittest.cc
index 3f522c5..04770cc 100644
--- a/chrome/browser/ash/crosapi/browser_manager_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_manager_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/component_updater/mock_component_updater_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -79,8 +80,9 @@
 
   void SimulateLacrosTermination() {
     SetStatePublic(State::TERMINATING);
-    if (browser_service_.has_value())
+    if (browser_service_.has_value()) {
       OnBrowserServiceDisconnected(*crosapi_id_, browser_service_->mojo_id);
+    }
     OnLacrosChromeTerminated();
   }
 
@@ -192,15 +194,57 @@
     crosapi::browser_util::ClearLacrosAvailabilityCacheForTest();
   }
 
-  void AddRegularUser(const std::string& email) {
+  enum class UserType {
+    kRegularUser = 0,
+    kWebKiosk = 1,
+    kChromeAppKiosk = 2,
+    kMaxValue = kChromeAppKiosk,
+  };
+
+  void AddUser(UserType user_type) {
+    const std::string email = "user@test.com";
     AccountId account_id = AccountId::FromUserEmail(email);
-    const User* user = fake_user_manager_->AddUser(account_id);
+
+    User* user;
+    switch (user_type) {
+      case UserType::kRegularUser:
+        user = fake_user_manager_->AddUser(account_id);
+        break;
+      case UserType::kWebKiosk:
+        user = fake_user_manager_->AddWebKioskAppUser(account_id);
+        break;
+      case UserType::kChromeAppKiosk:
+        user = fake_user_manager_->AddKioskAppUser(account_id);
+        break;
+    }
+
     fake_user_manager_->UserLoggedIn(account_id, user->username_hash(),
                                      /*browser_restart=*/false,
                                      /*is_child=*/false);
     fake_user_manager_->SimulateUserProfileLoad(account_id);
     ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
         user, &testing_profile_);
+
+    browser_util::SetProfileMigrationCompletedForUser(
+        local_state_.Get(),
+        ash::ProfileHelper::Get()
+            ->GetUserByProfile(&testing_profile_)
+            ->username_hash(),
+        browser_util::MigrationMode::kCopy);
+
+    EXPECT_TRUE(browser_util::IsLacrosEnabled());
+    EXPECT_TRUE(browser_util::IsLacrosAllowedToLaunch());
+  }
+
+  void ExpectCallingLoad(browser_util::LacrosSelection load_selection =
+                             browser_util::LacrosSelection::kRootfs,
+                         const std::string& lacros_path = "/run/lacros") {
+    EXPECT_CALL(*browser_loader_, Load(_))
+        .WillOnce([load_selection, lacros_path](
+                      BrowserLoader::LoadCompletionCallback callback) {
+          std::move(callback).Run(base::FilePath(lacros_path), load_selection,
+                                  base::Version());
+        });
   }
 
  protected:
@@ -227,27 +271,14 @@
 };
 
 TEST_F(BrowserManagerTest, LacrosKeepAlive) {
-  AddRegularUser("user@test.com");
-  browser_util::SetProfileMigrationCompletedForUser(
-      local_state_.Get(),
-      ash::ProfileHelper::Get()
-          ->GetUserByProfile(&testing_profile_)
-          ->username_hash(),
-      browser_util::MigrationMode::kCopy);
-  EXPECT_TRUE(browser_util::IsLacrosEnabled());
-  EXPECT_TRUE(browser_util::IsLacrosAllowedToLaunch());
+  AddUser(UserType::kRegularUser);
 
   using State = BrowserManagerFake::State;
   EXPECT_EQ(fake_browser_manager_->start_count(), 0);
 
   // Attempt to mount the Lacros image. Will not start as it does not meet the
   // automatic start criteria.
-  EXPECT_CALL(*browser_loader_, Load(_))
-      .WillOnce([](BrowserLoader::LoadCompletionCallback callback) {
-        std::move(callback).Run(base::FilePath("/run/lacros"),
-                                browser_util::LacrosSelection::kRootfs,
-                                base::Version());
-      });
+  ExpectCallingLoad();
   fake_browser_manager_->InitializeAndStartIfNeeded();
   EXPECT_EQ(fake_browser_manager_->start_count(), 0);
 
@@ -274,22 +305,8 @@
 }
 
 TEST_F(BrowserManagerTest, LacrosKeepAliveReloadsWhenUpdateAvailable) {
-  AddRegularUser("user@test.com");
-  browser_util::SetProfileMigrationCompletedForUser(
-      local_state_.Get(),
-      ash::ProfileHelper::Get()
-          ->GetUserByProfile(&testing_profile_)
-          ->username_hash(),
-      browser_util::MigrationMode::kCopy);
-  EXPECT_TRUE(browser_util::IsLacrosEnabled());
-  EXPECT_TRUE(browser_util::IsLacrosAllowedToLaunch());
-
-  EXPECT_CALL(*browser_loader_, Load(_))
-      .WillOnce([](BrowserLoader::LoadCompletionCallback callback) {
-        std::move(callback).Run(base::FilePath("/run/lacros"),
-                                browser_util::LacrosSelection::kRootfs,
-                                base::Version());
-      });
+  AddUser(UserType::kRegularUser);
+  ExpectCallingLoad();
   fake_browser_manager_->InitializeAndStartIfNeeded();
 
   using State = BrowserManagerFake::State;
@@ -305,12 +322,8 @@
   std::unique_ptr<BrowserManager::ScopedKeepAlive> keep_alive =
       fake_browser_manager_->KeepAlive(BrowserManager::Feature::kTestOnly);
 
-  EXPECT_CALL(*browser_loader_, Load(_))
-      .WillOnce([](BrowserLoader::LoadCompletionCallback callback) {
-        std::move(callback).Run(base::FilePath(kSampleLacrosPath),
-                                browser_util::LacrosSelection::kStateful,
-                                base::Version());
-      });
+  ExpectCallingLoad(browser_util::LacrosSelection::kStateful,
+                    kSampleLacrosPath);
 
   // On simulated termination, KeepAlive restarts Lacros. Since there is an
   // update, it should first load the updated image.
@@ -320,22 +333,8 @@
 }
 
 TEST_F(BrowserManagerTest, NewWindowReloadsWhenUpdateAvailable) {
-  AddRegularUser("user@test.com");
-  browser_util::SetProfileMigrationCompletedForUser(
-      local_state_.Get(),
-      ash::ProfileHelper::Get()
-          ->GetUserByProfile(&testing_profile_)
-          ->username_hash(),
-      browser_util::MigrationMode::kCopy);
-  EXPECT_TRUE(browser_util::IsLacrosEnabled());
-  EXPECT_TRUE(browser_util::IsLacrosAllowedToLaunch());
-
-  EXPECT_CALL(*browser_loader_, Load(_))
-      .WillOnce([](BrowserLoader::LoadCompletionCallback callback) {
-        std::move(callback).Run(base::FilePath("/run/lacros"),
-                                browser_util::LacrosSelection::kRootfs,
-                                base::Version());
-      });
+  AddUser(UserType::kRegularUser);
+  ExpectCallingLoad();
   fake_browser_manager_->InitializeAndStartIfNeeded();
 
   // Set the state of the browser manager as stopped, which would match the
@@ -360,28 +359,14 @@
 
 TEST_F(BrowserManagerTest, LacrosKeepAliveDoesNotBlockRestart) {
   EXPECT_CALL(mock_browser_service_, UpdateKeepAlive(_)).Times(0);
-
-  AddRegularUser("user@test.com");
-  browser_util::SetProfileMigrationCompletedForUser(
-      local_state_.Get(),
-      ash::ProfileHelper::Get()
-          ->GetUserByProfile(&testing_profile_)
-          ->username_hash(),
-      browser_util::MigrationMode::kCopy);
-  EXPECT_TRUE(browser_util::IsLacrosEnabled());
-  EXPECT_TRUE(browser_util::IsLacrosAllowedToLaunch());
+  AddUser(UserType::kRegularUser);
 
   using State = BrowserManagerFake::State;
   EXPECT_EQ(fake_browser_manager_->start_count(), 0);
 
   // Attempt to mount the Lacros image. Will not start as it does not meet the
   // automatic start criteria.
-  EXPECT_CALL(*browser_loader_, Load(_))
-      .WillOnce([](BrowserLoader::LoadCompletionCallback callback) {
-        std::move(callback).Run(base::FilePath("/run/lacros"),
-                                browser_util::LacrosSelection::kRootfs,
-                                base::Version());
-      });
+  ExpectCallingLoad();
   fake_browser_manager_->InitializeAndStartIfNeeded();
   EXPECT_EQ(fake_browser_manager_->start_count(), 0);
 
@@ -431,4 +416,28 @@
   EXPECT_EQ(fake_browser_manager_->start_count(), 4);
 }
 
+// In the Kiosk session, the Lacros window is created during the kiosk launch,
+// no need to create a new window in this case.
+TEST_F(BrowserManagerTest, DoNotOpenNewLacrosWindowInChromeAppKiosk) {
+  AddUser(UserType::kChromeAppKiosk);
+  ExpectCallingLoad();
+
+  fake_browser_manager_->InitializeAndStartIfNeeded();
+
+  EXPECT_CALL(mock_browser_service_, NewWindow(_, _, _, _)).Times(0);
+
+  fake_browser_manager_->SimulateLacrosStart(&mock_browser_service_);
+}
+
+TEST_F(BrowserManagerTest, DoNotOpenNewLacrosWindowInWebKiosk) {
+  AddUser(UserType::kWebKiosk);
+  ExpectCallingLoad();
+
+  fake_browser_manager_->InitializeAndStartIfNeeded();
+
+  EXPECT_CALL(mock_browser_service_, NewWindow(_, _, _, _)).Times(0);
+
+  fake_browser_manager_->SimulateLacrosStart(&mock_browser_service_);
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index cf2eee9..1971636 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -433,12 +433,6 @@
     return false;
   }
 
-  // Profile migration is only available for is Lacros is the only browser i.e.
-  // LacrosOnly mode.
-  if (IsAshWebBrowserEnabled()) {
-    return false;
-  }
-
   // If migration is already completed, it is not necessary to run again.
   if (IsCopyOrMoveProfileMigrationCompletedForUser(
           user_manager->GetLocalState(), user->username_hash())) {
diff --git a/chrome/browser/ash/crosapi/prefs_ash.cc b/chrome/browser/ash/crosapi/prefs_ash.cc
index f7b9f91..60a843dd 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.cc
+++ b/chrome/browser/ash/crosapi/prefs_ash.cc
@@ -53,6 +53,10 @@
           {mojom::PrefPath::kApplicationLocale,
            language::prefs::kApplicationLocale},
           {mojom::PrefPath::kSharedStorage, prefs::kSharedStorage},
+          {mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount,
+           ash::prefs::kMultitaskMenuNudgeClamshellShownCount},
+          {mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown,
+           ash::prefs::kMultitaskMenuNudgeClamshellLastShown},
       });
   auto pref_name = profile_prefpath_to_name->find(path);
   DCHECK(pref_name != profile_prefpath_to_name->end());
@@ -251,7 +255,9 @@
     case mojom::PrefPath::kQuickAnswersNoticeImpressionDuration:
     case mojom::PrefPath::kPreferredLanguages:
     case mojom::PrefPath::kApplicationLocale:
-    case mojom::PrefPath::kSharedStorage: {
+    case mojom::PrefPath::kSharedStorage:
+    case mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount:
+    case mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown: {
       if (!profile_prefs_registrar_) {
         LOG(WARNING) << "Primary profile is not yet initialized";
         return absl::nullopt;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn b/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
index 0b6decd..99477cf7 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
@@ -39,6 +39,8 @@
     "target_device_connection_broker_factory.h",
     "target_device_connection_broker_impl.cc",
     "target_device_connection_broker_impl.h",
+    "wifi_credentials.cc",
+    "wifi_credentials.h",
   ]
 }
 
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
index 2de6803..64f0fa16 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
@@ -103,6 +103,11 @@
   SendPayload(message_payload);
 }
 
+void AuthenticatedConnection::RequestWifiCredentials(
+    RequestWifiCredentialsCallback callback) {
+  NOTIMPLEMENTED();
+}
+
 void AuthenticatedConnection::SendBootstrapOptions(
     ConnectionResponseCallback callback) {
   base::Value::Dict bootstrap_options;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
index 1e37bd6..e6e8a255 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
+#include "chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
 #include "components/cbor/values.h"
 #include "url/origin.h"
@@ -26,6 +27,9 @@
   using RequestAccountTransferAssertionCallback =
       base::OnceCallback<void(absl::optional<FidoAssertionInfo>)>;
 
+  using RequestWifiCredentialsCallback =
+      base::OnceCallback<void(absl::optional<WifiCredentials>)>;
+
   explicit AuthenticatedConnection(NearbyConnection* nearby_connection);
   AuthenticatedConnection(AuthenticatedConnection&) = delete;
   AuthenticatedConnection& operator=(AuthenticatedConnection&) = delete;
@@ -35,6 +39,8 @@
       const std::string& challenge_b64url,
       RequestAccountTransferAssertionCallback callback);
 
+  void RequestWifiCredentials(RequestWifiCredentialsCallback callback);
+
   void NotifySourceOfUpdate();
 
  private:
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.cc
new file mode 100644
index 0000000..a6ed628f
--- /dev/null
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.cc
@@ -0,0 +1,18 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "wifi_credentials.h"
+
+namespace ash::quick_start {
+
+WifiCredentials::WifiCredentials() = default;
+
+WifiCredentials::~WifiCredentials() = default;
+
+WifiCredentials::WifiCredentials(const WifiCredentials& other) = default;
+
+WifiCredentials& WifiCredentials::operator=(const WifiCredentials& other) =
+    default;
+
+}  // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.h
new file mode 100644
index 0000000..0767a203
--- /dev/null
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.h
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_WIFI_CREDENTIALS_H_
+#define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_WIFI_CREDENTIALS_H_
+
+#include <string>
+
+namespace ash::quick_start {
+
+// A `struct` to store the information related to a device's Wifi Credentials.
+// It is constructed from the information returned by whatever device is
+// helping setup this device, and is used in the Quick Start flow to configure
+// the device's Wifi connection.
+struct WifiCredentials {
+  WifiCredentials();
+  WifiCredentials(const WifiCredentials& other);
+  WifiCredentials& operator=(const WifiCredentials& other);
+  ~WifiCredentials();
+
+  // The SSID of the Wifi Network.
+  std::string ssid;
+
+  // The password of the Wifi Network.
+  std::string password;
+
+  // Whether this is a hidden Wifi Network.
+  bool is_hidden;
+
+  // The Security Type of the Wifi Network.
+  std::string security_type;
+};
+
+}  // namespace ash::quick_start
+
+#endif  // CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_WIFI_CREDENTIALS_H_
diff --git a/chrome/browser/ash/login/saml/saml_browsertest.cc b/chrome/browser/ash/login/saml/saml_browsertest.cc
index ff4c582..10f1e5e 100644
--- a/chrome/browser/ash/login/saml/saml_browsertest.cc
+++ b/chrome/browser/ash/login/saml/saml_browsertest.cc
@@ -1122,6 +1122,10 @@
 
  protected:
   policy::DevicePolicyCrosTestHelper test_helper_;
+  // TODO(b/270930387): refactor to do device policy updates through
+  // `device_state_` from `SamlTestBase` instead. Right now we have two ways to
+  // do device policy updates in this fixture and they are not interchangeable
+  // as they update different policy blobs.
   policy::DevicePolicyBuilder* device_policy_;
   NiceMock<policy::MockConfigurationPolicyProvider> provider_;
   net::CookieList cookie_list_;
@@ -1760,6 +1764,58 @@
   SigninFrameJS().ExpectEQ("navigator.language", std::string(kLoginLocale));
 }
 
+// For tests relying on sso_profile in device policy blob which is responcible
+// for per-OU IdP configuration.
+class SsoProfileTest : public SamlTestBase {
+ public:
+  SsoProfileTest();
+
+  SsoProfileTest(const SsoProfileTest&) = delete;
+  SsoProfileTest& operator=(const SsoProfileTest&) = delete;
+
+  ~SsoProfileTest() override = default;
+};
+
+SsoProfileTest::SsoProfileTest() {
+  device_state_.SetState(
+      DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED);
+  std::unique_ptr<ScopedDevicePolicyUpdate> policy_update =
+      device_state_.RequestDevicePolicyUpdate();
+  // Set LoginAuthenticationBehavior policy to go directly to 3P IdP login page
+  em::ChromeDeviceSettingsProto& proto(*policy_update->policy_payload());
+  proto.mutable_login_authentication_behavior()
+      ->set_login_authentication_behavior(
+          em::LoginAuthenticationBehaviorProto_LoginBehavior_SAML_INTERSTITIAL);
+  // Set SSO Profile corresponding to `fake_saml_idp()`
+  policy_update->policy_data()->set_sso_profile(
+      fake_saml_idp()->GetIdpSsoProfile());
+}
+
+// Tests that we land on 3P IdP page corresponding to sso_profile from the
+// device policy blob during "add user" flow.
+IN_PROC_BROWSER_TEST_F(SsoProfileTest, AddNewUser) {
+  fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
+
+  // Set wrong redirect url for domain-based saml redirection. This ensures that
+  // for test to finish successfully it should perform redirection based on sso
+  // profile.
+  const GURL wrong_redirect_url("https://wrong.com");
+  fake_gaia_.fake_gaia()->RegisterSamlDomainRedirectUrl(
+      fake_saml_idp()->GetIdpDomain(), wrong_redirect_url);
+
+  // Launch "add user" flow.
+  ASSERT_TRUE(LoginScreenTestApi::ClickAddUserButton());
+  OobeScreenWaiter(GaiaView::kScreenId).Wait();
+  test::OobeJS().CreateVisibilityWaiter(true, kSigninFrameDialog)->Wait();
+  test::OobeJS().CreateVisibilityWaiter(false, kGaiaLoading)->Wait();
+
+  SigninFrameJS().TypeIntoPath("fake_user", {"Email"});
+  SigninFrameJS().TypeIntoPath("fake_password", {"Password"});
+
+  SigninFrameJS().TapOn("Submit");
+  test::WaitForPrimaryUserSessionStart();
+}
+
 class SAMLPasswordAttributesTest : public SAMLPolicyTest,
                                    public testing::WithParamInterface<bool> {
  public:
diff --git a/chrome/browser/ash/login/session/user_session_initializer.cc b/chrome/browser/ash/login/session/user_session_initializer.cc
index b1b874d..580559a 100644
--- a/chrome/browser/ash/login/session/user_session_initializer.cc
+++ b/chrome/browser/ash/login/session/user_session_initializer.cc
@@ -42,6 +42,7 @@
 #include "chrome/browser/ui/ash/calendar/calendar_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/clipboard_image_model_factory_impl.h"
 #include "chrome/browser/ui/ash/glanceables/chrome_glanceables_delegate.h"
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/media_client_impl.h"
 #include "chrome/browser/ui/webui/settings/ash/peripheral_data_access_handler.h"
@@ -259,6 +260,9 @@
   // created one per user in a multiprofile session.
   CalendarKeyedServiceFactory::GetInstance()->GetService(profile);
 
+  // Ensure that the `GlanceablesKeyedService` for `profile` is created.
+  GlanceablesKeyedServiceFactory::GetInstance()->GetService(profile);
+
   if (is_primary_user) {
     DCHECK_EQ(primary_profile_, profile);
 
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index a234c1d..c82a879 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -207,8 +207,6 @@
 #include "chromeos/ash/components/timezone/timezone_provider.h"
 #include "chromeos/ash/components/timezone/timezone_request.h"
 #include "chromeos/ash/services/rollback_network_config/public/mojom/rollback_network_config.mojom.h"
-#include "components/crash/core/app/breakpad_linux.h"
-#include "components/crash/core/app/crashpad.h"
 #include "components/metrics/structured/neutrino_logging.h"
 #include "components/metrics/structured/neutrino_logging_util.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chrome/browser/ash/policy/reporting/os_updates/os_updates_reporter_browsertest.cc b/chrome/browser/ash/policy/reporting/os_updates/os_updates_reporter_browsertest.cc
new file mode 100644
index 0000000..bc609a9
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/os_updates/os_updates_reporter_browsertest.cc
@@ -0,0 +1,183 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "ash/constants/ash_switches.h"
+#include "chrome/browser/ash/login/session/user_session_manager_test_api.h"
+#include "chrome/browser/ash/login/test/login_manager_mixin.h"
+#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
+#include "chrome/browser/ash/login/test/user_policy_mixin.h"
+#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
+#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
+#include "chrome/browser/policy/messaging_layer/proto/synced/os_events.pb.h"
+#include "chrome/test/base/fake_gaia_mixin.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
+#include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/dbus/missive/missive_client.h"
+#include "chromeos/dbus/missive/missive_client_test_observer.h"
+#include "components/reporting/proto/synced/record.pb.h"
+#include "components/reporting/proto/synced/record_constants.pb.h"
+#include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::chromeos::MissiveClientTestObserver;
+using ::reporting::Destination;
+using ::reporting::Priority;
+using ::reporting::Record;
+using ::testing::Eq;
+
+namespace ash::reporting {
+namespace {
+
+constexpr char kTestUserEmail[] = "test@example.com";
+constexpr char kTestAffiliationId[] = "test_affiliation_id";
+constexpr char kNewPlatformVersion[] = "1235.0.0";
+static const AccountId kTestAccountId = AccountId::FromUserEmailGaiaId(
+    kTestUserEmail,
+    signin::GetTestGaiaIdForEmail(kTestUserEmail));
+
+struct OsUpdatesReporterBrowserTestCase {
+  update_engine::Operation operation;
+  bool enterprise_rollback;
+};
+
+Record GetNextOsEventsRecord(MissiveClientTestObserver* observer) {
+  std::tuple<Priority, Record> enqueued_record =
+      observer->GetNextEnqueuedRecord();
+  Priority priority = std::get<0>(enqueued_record);
+  Record record = std::get<1>(enqueued_record);
+
+  EXPECT_THAT(priority, Eq(Priority::SECURITY));
+  return record;
+}
+
+class OsUpdatesReporterBrowserTest
+    : public policy::DevicePolicyCrosBrowserTest {
+ protected:
+  OsUpdatesReporterBrowserTest() {
+    login_manager_mixin_.AppendRegularUsers(1);
+    scoped_testing_cros_settings_.device_settings()->SetBoolean(
+        kReportOsUpdateStatus, true);
+  }
+
+  void SetUpOnMainThread() override {
+    login_manager_mixin_.set_should_launch_browser(true);
+    policy::DevicePolicyCrosBrowserTest::SetUpOnMainThread();
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    policy::DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
+    fake_update_engine_client_ =
+        ash::UpdateEngineClient::InitializeFakeForTest();
+
+    // Set up affiliation for the test user.
+    auto device_policy_update = device_state_.RequestDevicePolicyUpdate();
+    auto user_policy_update = user_policy_mixin_.RequestPolicyUpdate();
+
+    device_policy_update->policy_data()->add_device_affiliation_ids(
+        kTestAffiliationId);
+    user_policy_update->policy_data()->add_user_affiliation_ids(
+        kTestAffiliationId);
+  }
+
+  void SendFakeUpdateEngineStatus(const std::string& version,
+                                  bool is_rollback,
+                                  update_engine::Operation current_operation) {
+    update_engine::StatusResult status;
+    status.set_new_version(version);
+    status.set_is_enterprise_rollback(is_rollback);
+    status.set_will_powerwash_after_reboot(false);
+    status.set_current_operation(current_operation);
+    fake_update_engine_client_->set_default_status(status);
+    fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
+  }
+
+  ash::FakeSessionManagerClient* session_manager_client();
+
+  UserPolicyMixin user_policy_mixin_{&mixin_host_, kTestAccountId};
+
+  FakeGaiaMixin fake_gaia_mixin_{&mixin_host_};
+
+  LoginManagerMixin login_manager_mixin_{
+      &mixin_host_, LoginManagerMixin::UserList(), &fake_gaia_mixin_};
+
+  ScopedTestingCrosSettings scoped_testing_cros_settings_;
+
+  FakeUpdateEngineClient* fake_update_engine_client_ = nullptr;
+};
+
+IN_PROC_BROWSER_TEST_F(OsUpdatesReporterBrowserTest, ReportUpdateSuccessEvent) {
+  MissiveClientTestObserver observer(Destination::OS_EVENTS);
+
+  SendFakeUpdateEngineStatus(
+      /*version=*/kNewPlatformVersion, /*is_rollback=*/false,
+      /*current_operation=*/update_engine::Operation::UPDATED_NEED_REBOOT);
+
+  const Record& update_record = GetNextOsEventsRecord(&observer);
+  OsEventsRecord update_record_data;
+  ASSERT_TRUE(update_record_data.ParseFromString(update_record.data()));
+
+  ASSERT_TRUE(update_record_data.has_update_event());
+  EXPECT_TRUE(update_record_data.has_event_timestamp_sec());
+  EXPECT_EQ(update_record_data.target_os_version(), kNewPlatformVersion);
+  EXPECT_EQ(update_record_data.os_operation_type(),
+            reporting::OsOperationType::SUCCESS);
+}
+
+class OsUpdatesReporterBrowserErrorTest
+    : public OsUpdatesReporterBrowserTest,
+      public ::testing::WithParamInterface<OsUpdatesReporterBrowserTestCase> {
+ protected:
+  OsUpdatesReporterBrowserErrorTest() {}
+};
+
+IN_PROC_BROWSER_TEST_P(OsUpdatesReporterBrowserErrorTest, ReportErrorEvent) {
+  const auto test_case = GetParam();
+  MissiveClientTestObserver observer(Destination::OS_EVENTS);
+
+  SendFakeUpdateEngineStatus(
+      /*version=*/kNewPlatformVersion,
+      /*is_rollback=*/test_case.enterprise_rollback,
+      /*current_operation=*/test_case.operation);
+
+  const Record& update_record = GetNextOsEventsRecord(&observer);
+  OsEventsRecord update_record_data;
+  ASSERT_TRUE(update_record_data.ParseFromString(update_record.data()));
+
+  if (test_case.enterprise_rollback) {
+    ASSERT_TRUE(update_record_data.has_rollback_event());
+  } else {
+    ASSERT_TRUE(update_record_data.has_update_event());
+  }
+
+  EXPECT_TRUE(update_record_data.has_event_timestamp_sec());
+  EXPECT_EQ(update_record_data.target_os_version(), kNewPlatformVersion);
+  // The reported os_operation_type is FAILURE regardless of the
+  // test_case.operation.
+  EXPECT_EQ(update_record_data.os_operation_type(),
+            reporting::OsOperationType::FAILURE);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    OsUpdatesReporterBrowserErrorTest,
+    ::testing::ValuesIn<OsUpdatesReporterBrowserTestCase>(
+        {{/*operation=*/update_engine::Operation::ERROR,
+          /*enterprise_rollback=*/true},
+         {/*operation=*/update_engine::Operation::ERROR,
+          /*enterprise_rollback=*/false},
+         {/*operation=*/update_engine::Operation::REPORTING_ERROR_EVENT,
+          /*enterprise_rollback=*/true},
+         {/*operation=*/update_engine::Operation::REPORTING_ERROR_EVENT,
+          /*enterprise_rollback=*/false}}));
+
+}  // namespace
+}  // namespace ash::reporting
diff --git a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
index e285593..e69d4cba 100644
--- a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
@@ -6,7 +6,10 @@
 
 #include <memory>
 
+#include "ash/constants/ash_features.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/color_util.h"
 #include "ash/webui/firmware_update_ui/url_constants.h"
 #include "ash/webui/grit/ash_firmware_update_app_resources.h"
 #include "ash/webui/system_apps/public/system_web_app_type.h"
@@ -15,7 +18,7 @@
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/chromeos/styles/cros_styles.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/display/screen.h"
 
 namespace {
@@ -27,9 +30,18 @@
 // FirmwareUpdateApp's title bar and background needs to be bg-elevation-2 for
 // dark mode instead of the default dark mode background color.
 SkColor GetDarkModeBackgroundColor() {
-  return cros_styles::ResolveColor(cros_styles::ColorName::kBgColorElevation2,
-                                   /*is_dark_mode=*/true);
+  // This code will be deleted after Jelly launch.  Do NOT copy it! This was
+  // copied from system_web_app_install_utils.cc
+  ui::ColorProviderSource* color_provider_source =
+      ash::ColorUtil::GetColorProviderSourceForWindow(
+          ash::Shell::GetPrimaryRootWindow());
+  DCHECK(color_provider_source);
+  const ui::ColorProvider* color_provider =
+      color_provider_source->GetColorProvider();
+  DCHECK(color_provider);
+  return color_provider->GetColor(cros_tokens::kBgColorElevation2Dark);
 }
+
 }  // namespace
 
 // TODO(michaelcheco): Update to correct icon.
@@ -47,9 +59,13 @@
   info->user_display_mode = web_app::mojom::UserDisplayMode::kStandalone;
   info->theme_color =
       web_app::GetDefaultBackgroundColor(/*use_dark_mode=*/false);
-  info->dark_mode_theme_color = GetDarkModeBackgroundColor();
+  if (!ash::features::IsJellyEnabled()) {
+    // Once Jelly is launched, the theme and background colors for SWA are
+    // ignored and this can be deleted.
+    info->dark_mode_theme_color = GetDarkModeBackgroundColor();
+    info->dark_mode_background_color = info->dark_mode_theme_color;
+  }
   info->background_color = info->theme_color;
-  info->dark_mode_background_color = info->dark_mode_theme_color;
 
   return info;
 }
diff --git a/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
index 70719c3..91007c3 100644
--- a/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
@@ -72,7 +72,7 @@
   ProjectorAppClientImpl* projector_app_client =
       static_cast<ProjectorAppClientImpl*>(ash::ProjectorAppClient::Get());
   content::WebContents* annotator_embedder =
-      projector_app_client->get_annotator_message_handler_for_test()
+      projector_app_client->get_annotator_handler_for_test()
           ->get_web_ui_for_test()
           ->GetWebContents();
   PrepareAppForTest(annotator_embedder);
diff --git a/chrome/browser/autofill/content_autofill_driver_browsertest.cc b/chrome/browser/autofill/content_autofill_driver_browsertest.cc
index 5c2c0a8..3ff1836 100644
--- a/chrome/browser/autofill/content_autofill_driver_browsertest.cc
+++ b/chrome/browser/autofill/content_autofill_driver_browsertest.cc
@@ -160,7 +160,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
-                       SwitchTabAndHideAutofillPopup) {
+                       DISABLED_SwitchTabAndHideAutofillPopup) {
   EXPECT_CALL(autofill_client(),
               HideAutofillPopup(PopupHidingReason::kTabGone));
 
@@ -173,7 +173,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
-                       SameDocumentNavigationHideAutofillPopup) {
+                       DISABLED_SameDocumentNavigationHideAutofillPopup) {
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(),
       embedded_test_server()->GetURL("/autofill/autofill_test_form.html")));
@@ -196,7 +196,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
-                       PrerenderNavigationDoesntHideAutofillPopup) {
+                       DISABLED_PrerenderNavigationDoesntHideAutofillPopup) {
   GURL initial_url =
       embedded_test_server()->GetURL("/autofill/autofill_test_form.html");
   GURL prerender_url = embedded_test_server()->GetURL("/empty.html");
@@ -221,7 +221,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
-                       SubframeNavigationDoesntHideAutofillPopup) {
+                       DISABLED_SubframeNavigationDoesntHideAutofillPopup) {
   // Main frame is on a.com, iframe is on b.com.
   GURL url = embedded_test_server()->GetURL(
       "a.com", "/autofill/cross_origin_iframe.html");
@@ -243,7 +243,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
-                       TestPageNavigationHidingAutofillPopup) {
+                       DISABLED_TestPageNavigationHidingAutofillPopup) {
   // HideAutofillPopup is called once for each navigation.
   EXPECT_CALL(autofill_client(),
               HideAutofillPopup(PopupHidingReason::kNavigation))
@@ -275,7 +275,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverPrerenderBrowserTest,
-                       PrerenderingDoesNotSubmitForm) {
+                       DISABLED_PrerenderingDoesNotSubmitForm) {
   GURL initial_url =
       embedded_test_server()->GetURL("/autofill/autofill_test_form.html");
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
diff --git a/chrome/browser/autofill/form_structure_browsertest.cc b/chrome/browser/autofill/form_structure_browsertest.cc
index 58bfc69..c1737a4 100644
--- a/chrome/browser/autofill/form_structure_browsertest.cc
+++ b/chrome/browser/autofill/form_structure_browsertest.cc
@@ -228,7 +228,10 @@
       {// TODO(crbug.com/1311937): Remove once launched.
        // This feature is part of the AutofillRefinedPhoneNumberTypes rollout.
        // As it is not supported on iOS yet, it is disabled.
-       features::kAutofillConsiderPhoneNumberSeparatorsValidLabels});
+       features::kAutofillConsiderPhoneNumberSeparatorsValidLabels,
+       // TODO(crbug.com/1317961): Remove once launched. This feature is
+       // disabled since it is not supported on iOS.
+       features::kAutofillAlwaysParsePlaceholders});
 }
 
 FormStructureBrowserTest::~FormStructureBrowserTest() = default;
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index a00df9b7..b6a072f9 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -249,6 +249,8 @@
 #include "ash/webui/personalization_app/personalization_app_ui.h"
 #include "ash/webui/personalization_app/search/search.mojom.h"
 #include "ash/webui/print_management/print_management_ui.h"
+#include "ash/webui/projector_app/mojom/annotator.mojom.h"
+#include "ash/webui/projector_app/trusted_projector_annotator_ui.h"
 #include "ash/webui/scanning/mojom/scanning.mojom.h"
 #include "ash/webui/scanning/scanning_ui.h"
 #include "ash/webui/shimless_rma/shimless_rma.h"
@@ -1208,6 +1210,10 @@
       ash::camera_app::mojom::CameraAppHelper, ash::CameraAppUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
+      ash::annotator::mojom::AnnotatorPageHandlerFactory,
+      ash::TrustedProjectorAnnotatorUI>(map);
+
+  RegisterWebUIControllerInterfaceBinder<
       ash::help_app::mojom::PageHandlerFactory, ash::HelpAppUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc
index c5a8c21..62fef8b 100644
--- a/chrome/browser/chrome_browser_main_linux.cc
+++ b/chrome/browser/chrome_browser_main_linux.cc
@@ -18,9 +18,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/chromium_strings.h"
-#include "components/crash/core/app/breakpad_linux.h"
-#include "components/crash/core/app/crashpad.h"
-#include "components/metrics/metrics_service.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/bluez_dbus_thread_manager.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e5845ca..959b84fe 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1525,8 +1525,22 @@
 
 #endif
 
+std::unique_ptr<blocked_content::PopupNavigationDelegate>
+CreatePopupNavigationDelegate(NavigateParams params) {
+  return std::make_unique<ChromePopupNavigationDelegate>(std::move(params));
+}
+
+ChromeContentBrowserClient::PopupNavigationDelegateFactory
+    g_popup_navigation_delegate_factory = &CreatePopupNavigationDelegate;
+
 }  // namespace
 
+// static
+ChromeContentBrowserClient::PopupNavigationDelegateFactory&
+ChromeContentBrowserClient::GetPopupNavigationDelegateFactoryForTesting() {
+  return g_popup_navigation_delegate_factory;
+}
+
 ChromeContentBrowserClient::ChromeContentBrowserClient() {
 #if BUILDFLAG(ENABLE_PLUGINS)
   extra_parts_.push_back(new ChromeContentBrowserClientPluginsPart);
@@ -3853,8 +3867,7 @@
   return !blocked_content::ConsiderForPopupBlocking(disposition) ||
          blocked_content::MaybeBlockPopup(
              web_contents, &opener_top_level_frame_url,
-             std::make_unique<ChromePopupNavigationDelegate>(
-                 std::move(nav_params)),
+             (*g_popup_navigation_delegate_factory)(std::move(nav_params)),
              nullptr /*=open_url_params*/, blocked_params.features(),
              HostContentSettingsMapFactory::GetForProfile(profile)) != nullptr;
 }
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 7ecb90a..cb52c65 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -57,6 +57,10 @@
 class URLLoaderThrottle;
 }  // namespace blink
 
+namespace blocked_content {
+class PopupNavigationDelegate;
+}  // namespace blocked_content
+
 namespace content {
 class BrowserContext;
 class RenderFrameHost;
@@ -104,6 +108,7 @@
 class ChromeSerialDelegate;
 class ChromeUsbDelegate;
 class ChromeWebAuthenticationDelegate;
+struct NavigateParams;
 
 #if BUILDFLAG(ENABLE_VR)
 namespace vr {
@@ -113,6 +118,13 @@
 
 class ChromeContentBrowserClient : public content::ContentBrowserClient {
  public:
+  using PopupNavigationDelegateFactory =
+      std::unique_ptr<blocked_content::PopupNavigationDelegate> (*)(
+          NavigateParams);
+
+  static PopupNavigationDelegateFactory&
+  GetPopupNavigationDelegateFactoryForTesting();
+
   ChromeContentBrowserClient();
 
   ChromeContentBrowserClient(const ChromeContentBrowserClient&) = delete;
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc
index b067b24..bb88e9dc 100644
--- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc
+++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc
@@ -59,11 +59,11 @@
               extensions::api::app_runtime::OnLaunched::kEventName);
     ASSERT_EQ(1u, event.event_args.size());
 
-    const base::Value& launch_data = event.event_args[0];
-    const base::Value* is_kiosk_session =
-        launch_data.FindKeyOfType("isKioskSession", base::Value::Type::BOOLEAN);
+    const base::Value::Dict& launch_data = event.event_args[0].GetDict();
+    absl::optional<bool> is_kiosk_session =
+        launch_data.FindBool("isKioskSession");
     ASSERT_TRUE(is_kiosk_session);
-    EXPECT_TRUE(is_kiosk_session->GetBool());
+    EXPECT_TRUE(*is_kiosk_session);
 
     launched_apps_.push_back(extension_id);
   }
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
index b1fa4df..7d952a56 100644
--- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
+++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
@@ -93,7 +93,6 @@
     private HybridListRenderer mHybridListRenderer;
     private SurfaceScope mSurfaceScope;
     private FeedSurfaceScopeDependencyProvider mDependencyProvider;
-    private byte[] mWebFeedId;
     private PropertyModel mCreatorModel;
     private PropertyModelChangeProcessor<PropertyModel, CreatorProfileView, PropertyKey>
             mCreatorProfileModelChangeProcessor;
@@ -109,7 +108,6 @@
     private ViewGroup mLayout;
     private Profile mProfile;
     private Stream mStream;
-    private String mTitle;
     private int mHeaderCount;
 
     private EmptyBottomSheetObserver mSheetObserver;
@@ -136,7 +134,6 @@
      * @param snackbarManager the snackbarManager that is used for the feed.
      * @param windowAndroid the window needed by the feed
      * @param profile The Profile of the user.
-     * @param title The title used by the creator profile.
      * @param url the url used by the creator profile.
      * @param creatorWebContents the interface to generate webcontents for the bottomsheet.
      * @param creatorOpenTab the interface to open urls in a new tab, used by the bottomsheet.
@@ -144,16 +141,14 @@
      *         bottomsheet.
      */
     public CreatorCoordinator(Activity activity, byte[] webFeedId, SnackbarManager snackbarManager,
-            WindowAndroid windowAndroid, Profile profile, String title, String url,
+            WindowAndroid windowAndroid, Profile profile, String url,
             WebContentsCreator creatorWebContents, NewTabCreator creatorOpenTab,
             UnownedUserDataSupplier<ShareDelegate> bottomsheetShareDelegateSupplier,
             int entryPoint) {
         mActivity = activity;
-        mWebFeedId = webFeedId;
         mProfile = profile;
         mSnackbarManager = snackbarManager;
         mWindowAndroid = windowAndroid;
-        mTitle = title;
         mRecyclerView = setUpView();
         mCreatorWebContents = creatorWebContents;
         mCreatorOpenTab = creatorOpenTab;
@@ -176,13 +171,11 @@
         mLayoutView.addView(mRecyclerView);
 
         // Generate Creator Model
-        mCreatorModel = generateCreatorModel(mWebFeedId, mTitle, url);
-
-        // TODO(crbug.com/1377069): Add a JNI to get the follow status from CreatorBridge instead
-        if (mWebFeedId != null) {
+        mCreatorModel = generateCreatorModel(webFeedId, url);
+        // Attempt to avoid possible extra query if we already have metadata.
+        if (webFeedId != null) {
             getWebFeedMetadata();
         }
-        initBottomSheet();
 
         mCreatorProfileModelChangeProcessor = PropertyModelChangeProcessor.create(
                 mCreatorModel, (CreatorProfileView) mProfileView, CreatorProfileViewBinder::bind);
@@ -203,15 +196,32 @@
     public void queryFeedStream(FeedActionDelegate feedActionDelegate,
             HelpAndFeedbackLauncher helpAndFeedbackLauncher,
             Supplier<ShareDelegate> shareDelegateSupplier) {
-        if (mWebFeedId == null) {
+        if (mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY) == null) {
             Callback<QueryResult> queryWebFeedIdCallback = result -> {
-                mWebFeedId = result.webFeedId.getBytes();
                 mCreatorModel.set(CreatorProperties.WEB_FEED_ID_KEY, result.webFeedId.getBytes());
+                mCreatorModel.set(CreatorProperties.TITLE_KEY, result.title);
+                if (TextUtils.isEmpty(mCreatorModel.get(CreatorProperties.URL_KEY))) {
+                    mCreatorModel.set(CreatorProperties.URL_KEY, result.url);
+                    mCreatorModel.set(CreatorProperties.FORMATTED_URL_KEY,
+                            UrlFormatter.formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
+                                    new GURL(result.url)));
+                }
                 initFeedStream(feedActionDelegate, helpAndFeedbackLauncher, shareDelegateSupplier);
             };
-            WebFeedBridge.queryWebFeedId(
+            WebFeedBridge.queryWebFeed(
                     mCreatorModel.get(CreatorProperties.URL_KEY), queryWebFeedIdCallback);
-        } else {
+        } else if (TextUtils.isEmpty(mCreatorModel.get(CreatorProperties.TITLE_KEY))
+                || TextUtils.isEmpty(mCreatorModel.get(CreatorProperties.URL_KEY))) {
+            Callback<QueryResult> queryWebFeedIdCallback = result -> {
+                mCreatorModel.set(CreatorProperties.TITLE_KEY, result.title);
+                mCreatorModel.set(CreatorProperties.URL_KEY, result.url);
+                mCreatorModel.set(CreatorProperties.FORMATTED_URL_KEY,
+                        UrlFormatter.formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
+                                new GURL(result.url)));
+            };
+            WebFeedBridge.queryWebFeedId(
+                    new String(mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY)),
+                    queryWebFeedIdCallback);
             initFeedStream(feedActionDelegate, helpAndFeedbackLauncher, shareDelegateSupplier);
         }
     }
@@ -232,7 +242,8 @@
                 helpAndFeedbackLauncher,
                 /* FeedContentFirstLoadWatcher */ this,
                 /* streamsMediator */ new StreamsMediatorImpl(),
-                new SingleWebFeedParameters(mWebFeedId, mEntryPoint));
+                new SingleWebFeedParameters(
+                        mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY), mEntryPoint));
 
         if (mEntryPoint == SingleWebFeedEntryPoint.MENU) {
             mStream.addOnContentChangedListener(new ContentChangedListener());
@@ -306,12 +317,11 @@
         return mActivity.getResources().getDimensionPixelSize(R.dimen.content_previews_padding);
     }
 
-    private PropertyModel generateCreatorModel(byte[] webFeedId, String title, String url) {
+    private PropertyModel generateCreatorModel(byte[] webFeedId, String url) {
         String formattedUrl =
                 UrlFormatter.formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(new GURL(url));
         PropertyModel model = new PropertyModel.Builder(CreatorProperties.ALL_KEYS)
                                       .with(CreatorProperties.WEB_FEED_ID_KEY, webFeedId)
-                                      .with(CreatorProperties.TITLE_KEY, title)
                                       .with(CreatorProperties.URL_KEY, url)
                                       .with(CreatorProperties.IS_FOLLOWED_KEY, false)
                                       .with(CreatorProperties.IS_TOOLBAR_VISIBLE_KEY, false)
@@ -341,7 +351,8 @@
                                 result.visitUrl));
             }
         };
-        WebFeedBridge.getWebFeedMetadata(mWebFeedId, metadata_callback);
+        WebFeedBridge.getWebFeedMetadata(
+                mCreatorModel.get(CreatorProperties.WEB_FEED_ID_KEY), metadata_callback);
     }
 
     /**
@@ -446,7 +457,7 @@
 
         mPeeked = false;
         mFullyOpened = false;
-        mTabMediator.requestShowContent(url, mTitle);
+        mTabMediator.requestShowContent(url, mCreatorModel.get(CreatorProperties.TITLE_KEY));
     }
 
     @Override
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java
index bf8179a..88aad8d 100644
--- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java
+++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinatorTest.java
@@ -94,7 +94,6 @@
     private UrlFormatter.Natives mUrlFormatterJniMock;
 
     private final byte[] mWebFeedIdDefault = "webFeedId".getBytes();
-    private final String mTitleDefault = "Example";
     private final String mUrlDefault = JUnitTestGURLs.EXAMPLE_URL;
     private final String mTestUrl = JUnitTestGURLs.URL_1;
     private final int mEntryPointDefault = SingleWebFeedEntryPoint.OTHER;
@@ -116,24 +115,23 @@
         mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity);
     }
 
-    private CreatorCoordinator newCreatorCoordinator(
-            String title, String url, byte[] webFeedId, int entryPoint) {
+    private CreatorCoordinator newCreatorCoordinator(String url, byte[] webFeedId, int entryPoint) {
         return new CreatorCoordinator(mActivity, webFeedId, mSnackbarManager, mWindowAndroid,
-                mProfile, title, url, mCreatorWebContents, mCreatorOpenTab, mShareDelegateSupplier,
+                mProfile, url, mCreatorWebContents, mCreatorOpenTab, mShareDelegateSupplier,
                 entryPoint);
     }
 
     @Test
     public void testCreatorCoordinatorConstruction() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         assertNotNull("Could not construct CreatorCoordinator", creatorCoordinator);
     }
 
     @Test
     public void testActionBar() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         View outerView = creatorCoordinator.getView();
         ViewGroup actionBar = (ViewGroup) outerView.findViewById(R.id.action_bar);
         assertNotNull("Could not retrieve ActionBar", actionBar);
@@ -141,8 +139,8 @@
 
     @Test
     public void testOnChangeListener_noError() {
-        CreatorCoordinator coordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator coordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         coordinator.setStreamForTest(mStreamMock);
         ContentChangedListener listener = coordinator.new ContentChangedListener();
 
@@ -158,8 +156,8 @@
 
     @Test
     public void testOnChangeListener_error() {
-        CreatorCoordinator coordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator coordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         coordinator.setStreamForTest(mStreamMock);
         ContentChangedListener listener = coordinator.new ContentChangedListener();
 
@@ -175,35 +173,17 @@
 
     @Test
     public void testCreatorModel_Creation() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         assertNotNull("Could not retrieve CreatorModel", creatorModel);
     }
 
     @Test
-    public void testCreatorModel_DefaultTitle() {
-        String creatorTitle = "creatorTitle";
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                creatorTitle, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
-        PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
-        String modelTitle = creatorModel.get(CreatorProperties.TITLE_KEY);
-        assertEquals(creatorTitle, modelTitle);
-
-        View creatorProfileView = creatorCoordinator.getProfileView();
-        TextView profileTitleView = creatorProfileView.findViewById(R.id.creator_name);
-        assertEquals(creatorTitle, profileTitleView.getText());
-
-        View creatorView = creatorCoordinator.getView();
-        TextView toolbarTitleView = creatorView.findViewById(R.id.creator_title_toolbar);
-        assertEquals(creatorTitle, toolbarTitleView.getText());
-    }
-
-    @Test
     public void testCreatorModel_DefaultUrl() {
         String creatorUrl = mTestUrl;
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, creatorUrl, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(creatorUrl, mWebFeedIdDefault, mEntryPointDefault);
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         String modelUrl = creatorModel.get(CreatorProperties.URL_KEY);
         assertEquals(creatorUrl, modelUrl);
@@ -216,8 +196,8 @@
     @Test
     public void testCreatorModel_DefaultWebFeedId() {
         byte[] creatorWebFeedId = "creatorWebFeedId".getBytes();
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, creatorWebFeedId, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, creatorWebFeedId, mEntryPointDefault);
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         byte[] modelWebFeedId = creatorModel.get(CreatorProperties.WEB_FEED_ID_KEY);
         assertEquals(creatorWebFeedId, modelWebFeedId);
@@ -225,8 +205,8 @@
 
     @Test
     public void testCreatorModel_NewTitle() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         String newTitle = "creatorTitle 2.0";
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         creatorModel.set(CreatorProperties.TITLE_KEY, newTitle);
@@ -244,8 +224,8 @@
 
     @Test
     public void testCreatorModel_NewUrl() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         String newUrl = mTestUrl;
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         creatorModel.set(CreatorProperties.URL_KEY, newUrl);
@@ -259,8 +239,8 @@
 
     @Test
     public void testCreatorModel_ToolbarVisibility() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         View creatorView = creatorCoordinator.getView();
         FrameLayout mButtonsContainer = creatorView.findViewById(R.id.creator_all_buttons_toolbar);
@@ -272,8 +252,8 @@
 
     @Test
     public void testCreatorModel_IsFollowedStatus() {
-        CreatorCoordinator creatorCoordinator = newCreatorCoordinator(
-                mTitleDefault, mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
+        CreatorCoordinator creatorCoordinator =
+                newCreatorCoordinator(mUrlDefault, mWebFeedIdDefault, mEntryPointDefault);
         PropertyModel creatorModel = creatorCoordinator.getCreatorModel();
         View creatorProfileView = creatorCoordinator.getProfileView();
         ButtonCompat followButton = creatorProfileView.findViewById(R.id.creator_follow_button);
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java
index 10fbcfb5..e64c5dc 100644
--- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java
+++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorMediatorTest.java
@@ -95,7 +95,6 @@
     public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
             new ActivityScenarioRule<>(TestActivity.class);
 
-    private final String mTitle = "Example";
     private final String mUrl = JUnitTestGURLs.EXAMPLE_URL;
     private final byte[] mWebFeedId = "webFeedId".getBytes();
     private CreatorCoordinator mCreatorCoordinator;
@@ -119,7 +118,7 @@
 
         mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity);
         mCreatorCoordinator = new CreatorCoordinator(mActivity, mWebFeedId, mSnackbarManager,
-                mWindowAndroid, mProfile, mTitle, mUrl, mCreatorWebContents, mCreatorOpenTab,
+                mWindowAndroid, mProfile, mUrl, mCreatorWebContents, mCreatorOpenTab,
                 mShareDelegateSupplier, SingleWebFeedEntryPoint.OTHER);
         mCreatorModel = mCreatorCoordinator.getCreatorModel();
 
diff --git a/chrome/browser/downgrade/downgrade_manager.cc b/chrome/browser/downgrade/downgrade_manager.cc
index 576b00d..0de94fa 100644
--- a/chrome/browser/downgrade/downgrade_manager.cc
+++ b/chrome/browser/downgrade/downgrade_manager.cc
@@ -235,8 +235,7 @@
   DCHECK(!user_data_dir.empty());
   DCHECK_NE(type_, Type::kAdministrativeWipe);
   const base::StringPiece version(PRODUCT_VERSION);
-  base::WriteFile(GetLastVersionFile(user_data_dir), version.data(),
-                  version.size());
+  base::WriteFile(GetLastVersionFile(user_data_dir), version);
 }
 
 void DowngradeManager::DeleteMovedUserDataSoon(
diff --git a/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc b/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc
index c931e2b..39a9da6 100644
--- a/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc
@@ -66,19 +66,17 @@
                                                        browser_context());
   ASSERT_TRUE(result);
   ASSERT_TRUE(result->is_dict());
-  ASSERT_EQ(result->DictSize(), 2u);
+  const base::Value::Dict& result_dict = result->GetDict();
+  ASSERT_EQ(result_dict.size(), 2u);
 
-  const base::Value* val =
-      result->FindKeyOfType("manufacturer", base::Value::Type::STRING);
-  ASSERT_TRUE(val);
-  const std::string& manufacturer = val->GetString();
+  const std::string* manufacturer = result_dict.FindString("manufacturer");
+  ASSERT_TRUE(manufacturer);
 
-  val = result->FindKeyOfType("model", base::Value::Type::STRING);
-  ASSERT_TRUE(val);
-  const std::string& model = val->GetString();
+  const std::string* model = result_dict.FindString("model");
+  ASSERT_TRUE(model);
 
-  EXPECT_FALSE(manufacturer.empty());
-  EXPECT_FALSE(model.empty());
+  EXPECT_FALSE(manufacturer->empty());
+  EXPECT_FALSE(model->empty());
 }
 
 TEST_F(EnterpriseHardwarePlatformAPITest,
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
index 6edac894..682ba13 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -315,7 +315,7 @@
     AutocompleteInput input(input_string, metrics::OmniboxEventProto::NTP,
                             ChromeAutocompleteSchemeClassifier(profile()));
     autocomplete_controller->Start(input);
-    omnibox_view->model()->AcceptInput(disposition);
+    omnibox_view->model()->OpenSelection(base::TimeTicks(), disposition);
     WaitForAutocompleteDone(browser());
   };
 
@@ -426,14 +426,17 @@
                             metrics::OmniboxEventProto::NTP,
                             ChromeAutocompleteSchemeClassifier(profile()));
     GetAutocompleteController()->Start(input);
-    GetLocationBar(browser())->AcceptInput();
+    GetLocationBar(browser())->GetOmniboxView()->model()->OpenSelection();
   }
   {
     AutocompleteInput input(
         u"alpha word incognito", metrics::OmniboxEventProto::NTP,
         ChromeAutocompleteSchemeClassifier(incognito_profile));
     incognito_controller->Start(input);
-    GetLocationBar(incognito_browser)->AcceptInput();
+    GetLocationBar(incognito_browser)
+        ->GetOmniboxView()
+        ->model()
+        ->OpenSelection();
   }
 
   EXPECT_TRUE(on_the_record_listener.WaitUntilSatisfied());
@@ -478,7 +481,7 @@
   AutocompleteInput input(u"kw command", metrics::OmniboxEventProto::NTP,
                           ChromeAutocompleteSchemeClassifier(profile()));
   autocomplete_controller->Start(input);
-  location_bar->AcceptInput();
+  location_bar->GetOmniboxView()->model()->OpenSelection();
   WaitForAutocompleteDone(browser());
   EXPECT_TRUE(autocomplete_controller->done());
   // This checks that the keyword provider (via javascript)
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc
index 614e098..6d945fa 100644
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc
+++ b/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc
@@ -237,9 +237,8 @@
 
   const base::Value& group_info = groups_list[0];
   ASSERT_TRUE(group_info.is_dict());
-  EXPECT_EQ(
-      tab_groups_util::GetGroupId(group1),
-      group_info.FindKeyOfType("id", base::Value::Type::INTEGER)->GetInt());
+  EXPECT_EQ(tab_groups_util::GetGroupId(group1),
+            *group_info.GetDict().FindInt("id"));
 }
 
 // Test that querying groups by color returns the correct groups.
@@ -275,9 +274,8 @@
 
   const base::Value& group_info = groups_list[0];
   ASSERT_EQ(base::Value::Type::DICT, group_info.type());
-  EXPECT_EQ(
-      tab_groups_util::GetGroupId(group3),
-      group_info.FindKeyOfType("id", base::Value::Type::INTEGER)->GetInt());
+  EXPECT_EQ(tab_groups_util::GetGroupId(group3),
+            *group_info.GetDict().FindInt("id"));
 }
 
 // Test that getting a group returns the correct metadata.
diff --git a/chrome/browser/extensions/extension_action_runner_browsertest.cc b/chrome/browser/extensions/extension_action_runner_browsertest.cc
index 2e7c453c..d948c13f 100644
--- a/chrome/browser/extensions/extension_action_runner_browsertest.cc
+++ b/chrome/browser/extensions/extension_action_runner_browsertest.cc
@@ -742,8 +742,8 @@
 // is either 'on all sites' or 'on site'). Note that we don't check if extension
 // `WantsToRun` because on user-restricted sites, actions are blocked rather
 // than withheld.
-// TODO(crbug.com/1363781): Flaky on Win 7 and Mac 12.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+// TODO(crbug.com/1363781): Flaky on Mac 12.
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_HandleUserSiteSettingModified_ExtensionHasAccess \
   DISABLED_HandleUserSiteSettingModified_ExtensionHasAccess
 #else
@@ -866,8 +866,8 @@
 // is either 'on all sites' or 'on site'). Note that we don't check if extension
 // `WantsToRun` because on user-restricted sites, actions are blocked rather
 // than withheld.
-// TODO(crbug.com/1363781): Flaky on Win 7 and Mac 12.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+// TODO(crbug.com/1363781): Flaky on Mac 12.
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_HandleUserSiteSettingModified_ExtensionHasAccess \
   DISABLED_HandleUserSiteSettingModified_ExtensionHasAccess
 #else
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/CreatorIntentConstants.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/CreatorIntentConstants.java
index ccfa49c..fbfedf5 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/CreatorIntentConstants.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/CreatorIntentConstants.java
@@ -9,7 +9,6 @@
  */
 public final class CreatorIntentConstants {
     public static final String CREATOR_WEB_FEED_ID = "CREATOR_WEB_FEED_ID";
-    public static final String CREATOR_TITLE = "CREATOR_TITLE";
     public static final String CREATOR_URL = "CREATOR_URL";
     public static final String CREATOR_ENTRY_POINT = "CREATOR_ENTRY_POINT";
 
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
index 4f91d22..d1b35749 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
@@ -112,11 +112,20 @@
      * @param url The URL for which the status is being requested.
      * @param callback The callback to receive the Web Feed metadata, or null if it is not found.
      */
-    public static void queryWebFeedId(String url, Callback<QueryResult> callback) {
+    public static void queryWebFeed(String url, Callback<QueryResult> callback) {
         WebFeedBridgeJni.get().queryWebFeed(url, callback);
     }
 
     /**
+     * Returns the Web Feed id for the web feed associated with this page.
+     * @param url The URL for which the status is being requested.
+     * @param callback The callback to receive the Web Feed metadata, or null if it is not found.
+     */
+    public static void queryWebFeedId(String id, Callback<QueryResult> callback) {
+        WebFeedBridgeJni.get().queryWebFeedId(id, callback);
+    }
+
+    /**
      * Returns Web Feed metadata respective to the provided identifier. The callback will receive
      * `null` if no matching recommended or followed Web Feed is found.
      * @param webFeedId The idenfitier of the Web Feed.
@@ -189,11 +198,15 @@
     /** Container for results from an QueryWebFeed request. */
     public static class QueryResult {
         @CalledByNative("QueryResult")
-        public QueryResult(String webFeedId) {
+        public QueryResult(String webFeedId, String title, String url) {
             this.webFeedId = webFeedId;
+            this.title = title;
+            this.url = url;
         }
         // Result of the operation.
         public final String webFeedId;
+        public final String title;
+        public final String url;
     }
 
     /**
@@ -290,5 +303,6 @@
         void getRecentVisitCountsToHost(GURL url, Callback<int[]> callback);
         void incrementFollowedFromWebPageMenuCount();
         void queryWebFeed(String url, Callback<QueryResult> callback);
+        void queryWebFeedId(String id, Callback<QueryResult> callback);
     }
 }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItem.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItem.java
index 476cf39..a583bdc7 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItem.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItem.java
@@ -369,7 +369,6 @@
                 intent.putExtra(
                         CreatorIntentConstants.CREATOR_WEB_FEED_ID, mRecommendedWebFeedName);
             }
-            intent.putExtra(CreatorIntentConstants.CREATOR_TITLE, mTitle);
             intent.putExtra(CreatorIntentConstants.CREATOR_URL, mUrl.getSpec());
             intent.putExtra(
                     CreatorIntentConstants.CREATOR_ENTRY_POINT, SingleWebFeedEntryPoint.MENU);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java
index a79da14..579b144 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java
@@ -215,9 +215,7 @@
         verify(mContext).startActivity(mIntentCaptor.capture());
         Intent intent = mIntentCaptor.getValue();
         assertNotNull(intent);
-        assertEquals(3, intent.getExtras().size());
-        assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_TITLE));
-        assertNotNull(intent.getExtras().getString(CreatorIntentConstants.CREATOR_TITLE));
+        assertEquals(2, intent.getExtras().size());
         assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_URL));
         assertNotNull(intent.getExtras().getString(CreatorIntentConstants.CREATOR_URL));
         assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_ENTRY_POINT));
diff --git a/chrome/browser/feed/android/web_feed_bridge.cc b/chrome/browser/feed/android/web_feed_bridge.cc
index 95327a6..05b0249 100644
--- a/chrome/browser/feed/android/web_feed_bridge.cc
+++ b/chrome/browser/feed/android/web_feed_bridge.cc
@@ -137,7 +137,9 @@
     JNIEnv* env,
     const WebFeedSubscriptions::QueryWebFeedResult& result) {
   return Java_QueryResult_Constructor(
-      env, base::android::ConvertUTF8ToJavaString(env, result.web_feed_id));
+      env, base::android::ConvertUTF8ToJavaString(env, result.web_feed_id),
+      base::android::ConvertUTF8ToJavaString(env, result.title),
+      base::android::ConvertUTF8ToJavaString(env, result.url));
 }
 
 base::android::ScopedJavaLocalRef<jobject> ToJava(
@@ -386,4 +388,19 @@
       GURL(base::android::ConvertJavaStringToUTF8(env, url)),
       std::move(callback));
 }
+
+static void JNI_WebFeedBridge_QueryWebFeedId(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& id,
+    const base::android::JavaParamRef<jobject>& j_callback) {
+  base::OnceCallback<void(WebFeedSubscriptions::QueryWebFeedResult)> callback =
+      AdaptQueryWebFeedResultCallback(j_callback);
+  WebFeedSubscriptions* subscriptions = GetSubscriptions();
+  if (!subscriptions) {
+    std::move(callback).Run({});
+    return;
+  }
+  subscriptions->QueryWebFeedId(base::android::ConvertJavaStringToUTF8(env, id),
+                                std::move(callback));
+}
 }  // namespace feed
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d8aaab5..1308ffc 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -142,6 +142,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "ambient-mode-managed-screensaver-enabled",
+    "owners": [ "eariassoto", "fahadmansoor", "ghostbusters@google.com" ],
+    "expiry_milestone": 118
+  },
+  {
     "name": "android-force-app-language-prompt",
     "owners": [ "perrier", "chrome-language@google.com" ],
     "expiry_milestone": 115
@@ -169,7 +174,7 @@
   {
     "name": "apn-revamp",
     "owners": [ "gordonseto@google.com", "cros-connectivity@google.com" ],
-    "expiry_milestone": 113
+    "expiry_milestone": 128
   },
   {
     "name": "app-deduplication-service-fondue",
@@ -338,6 +343,11 @@
     "expiry_milestone": 122
   },
   {
+    "name": "ash-capture-mode-gif-recording",
+    "owners": [ "afakhry", "gzadina" ],
+    "expiry_milestone": 123
+  },
+  {
     "name": "ash-debug-shortcuts",
     "owners": [ "//ash/OWNERS" ],
     // Used by developers for debugging and to dump extra information to logs
@@ -1082,16 +1092,6 @@
     "expiry_milestone": 115
   },
   {
-    "name": "closed-tab-cache",
-    "owners": [
-      "altimin@chromium.org",
-      "sky@chromium.org",
-      "sreejakshetty@chromium.org",
-      "tobias.soppa@code.berlin"
-    ],
-    "expiry_milestone": 110
-  },
-  {
     "name": "cloud-ap-auth",
     "owners": [ "igorruvinov", "zmin" ],
     "expiry_milestone": 120
@@ -1858,6 +1858,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "enable-bookmarks-account-storage",
+    "owners": [ "jlebel", "bsazonov", "chrome-signin-team" ],
+    "expiry_milestone": 118
+  },
+  {
     "name": "enable-cardboard",
     "owners": [ "alcooper", "chrome-xr-eng@google.com"],
     "expiry_milestone": 120
@@ -3282,11 +3287,6 @@
     "expiry_milestone": 115
   },
   {
-    "name": "enable-user-cloud-signin-restriction-policy",
-    "owners": [ "ydago" ],
-    "expiry_milestone": 105
-  },
-  {
     "name": "enable-user-policy",
     "owners": [ "vincb" ],
     "expiry_milestone": 130
@@ -4018,7 +4018,7 @@
   {
     "name": "full-user-agent",
     "owners": [ "victortan", "abeyad"],
-    "expiry_milestone": 113
+    "expiry_milestone": 115
   },
   {
     "name": "fullscreen-promos-manager",
@@ -4779,7 +4779,7 @@
   {
     "name": "messages-for-android-infrastructure",
     "owners": [ "lazzzis", "aishwaryarj" ],
-    "expiry_milestone": 112
+    "expiry_milestone": 120
   },
   {
     "name": "messages-for-android-offer-notification",
@@ -4799,7 +4799,7 @@
   {
     "name": "messages-for-android-pwa-install",
     "owners": [ "lazzzis", "aishwaryarj" ],
-    "expiry_milestone": 112
+    "expiry_milestone": 120
   },
   {
     "name": "messages-for-android-save-card",
@@ -5028,7 +5028,7 @@
   {
     "name": "ntp-modules-first-run-experience",
     "owners": [ "mahmadi", "pauladedeji", "tiborg" ],
-    "expiry_milestone": 111
+    "expiry_milestone": 115
   },
   {
     "name": "ntp-modules-redesigned",
@@ -5838,7 +5838,7 @@
       "johnidel",
       "jkarlin",
       "pauljensen"],
-    "expiry_milestone": 112
+    "expiry_milestone": 116
   },
   {
     "name": "privacy-sandbox-settings-4",
@@ -6075,26 +6075,11 @@
     "expiry_milestone": 95
   },
   {
-    "name": "reduce-user-agent",
-    "owners": [ "aarontag", "miketaylr"],
-    "expiry_milestone": 113
-  },
-  {
     "name": "reduce-user-agent-android-version-device-model",
     "owners": [ "miketaylr", "victortan"],
     "expiry_milestone": 116
   },
   {
-    "name": "reduce-user-agent-minor-version",
-    "owners": [ "abeyad", "victortan"],
-    "expiry_milestone": 113
-  },
-  {
-    "name": "reduce-user-agent-platform-oscpu",
-    "owners": [ "miketaylr", "victortan"],
-    "expiry_milestone": 113
-  },
-  {
     "name": "reengagement-notification",
     "owners": [ "dtrainor", "xingliu" ],
     "expiry_milestone": 90
@@ -6110,11 +6095,6 @@
     "expiry_milestone": 110
   },
   {
-    "name": "related-searches-ui",
-    "owners": [ "gangwu", "related-searches-vteam@google.com" ],
-    "expiry_milestone": 110
-  },
-  {
     "name": "release-notes-notification-all-channels",
     "owners": [ "//ash/webui/help_app_ui/OWNERS" ],
     // This is required by test teams to verify functionality on dev/beta
@@ -7262,6 +7242,11 @@
     "expiry_milestone": 122
   },
   {
+    "name": "web-feed-feedback-reroute",
+    "owners": ["zekunjiang", "tinazwang", "chrome-with-friends@google.com"],
+    "expiry_milestone": 122
+  },
+  {
     "name": "web-feed-ios",
     "owners": [ "adamta", "sczs", "tinazwang" ],
     "expiry_milestone": 122
@@ -7385,7 +7370,7 @@
   {
     "name": "wifi-connect-mac-address-randomization",
     "owners": [ "jsiuda", "chromeos-wifi-team" ],
-    "expiry_milestone": 108
+    "expiry_milestone": 130
   },
   {
     "name": "win-10-tab-search-caption-button",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 87f076f..47b9abb 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -693,12 +693,6 @@
 const char kChromeRefresh2023Name[] = "Chrome Refresh 2023";
 const char kChromeRefresh2023Description[] = "Refresh of Chrome Desktop UI";
 
-const char kClosedTabCacheName[] = "Closed Tab Cache";
-const char kClosedTabCacheDescription[] =
-    "Enables closed tab cache to instantaneously restore recently closed tabs. "
-    "NOTE: This feature is highly experimental and will lead to various "
-    "breakages, enable at your own risk.";
-
 const char kCommerceHintAndroidName[] = "Commerce Hint Android";
 const char kCommerceHintAndroidDescription[] =
     "Enables commerce hint detection on Android.";
@@ -2638,12 +2632,6 @@
     "Reduce the amount of information available in the Accept-Language request "
     "header. See https://github.com/Tanych/accept-language for more info.";
 
-const char kReduceUserAgentName[] = "Reduce User-Agent request header";
-const char kReduceUserAgentDescription[] =
-    "Reduce (formerly, \"freeze\") the amount of information available in "
-    "the User-Agent request header. "
-    "See https://www.chromium.org/updates/ua-reduction for more info.";
-
 const char kRestrictGamepadAccessName[] = "Restrict gamepad access";
 const char kRestrictGamepadAccessDescription[] =
     "Enables Permissions Policy and Secure Context restrictions on the Gamepad "
@@ -3401,20 +3389,6 @@
 const char kDurableClientHintsCacheDescription[] =
     "Persist the client hints cache beyond browser restarts.";
 
-const char kReduceUserAgentMinorVersionName[] =
-    "Reduce the minor version in the User-Agent string";
-const char kReduceUserAgentMinorVersionDescription[] =
-    "Reduce the minor, build, and patch versions in the User-Agent string.  "
-    "The Chrome version in the User-Agent string will be reported as "
-    "Chrome/<major_version>.0.0.0.";
-
-const char kReduceUserAgentPlatformOsCpuName[] =
-    "Reduce the plaftform and oscpu in the desktop User-Agent string";
-const char kReduceUserAgentPlatformOsCpuDescription[] =
-    "Reduce the plaftform and oscpu in the desktop User-Agent string.  "
-    "The platform and oscpu in the User-Agent string will be reported as "
-    "<unifiedPlatform>";
-
 const char kSkipServiceWorkerFetchHandlerName[] =
     "Skip Service Worker Fetch Handler if skippable";
 const char kSkipServiceWorkerFetchHandlerDescription[] =
@@ -3930,13 +3904,6 @@
     "Enables requesting related searches suggestions. These will be requested "
     "but not shown unless the UI flag is also enabled.";
 
-const char kRelatedSearchesUiName[] =
-    "Forces showing of the Related Searches UI on Android";
-const char kRelatedSearchesUiDescription[] =
-    "Forces the Related Searches UI and underlying requests to be enabled "
-    "regardless of whether they are safe or useful. This requires the Related "
-    "Searches feature flag to also be enabled.";
-
 const char kRequestDesktopSiteAdditionsName[] =
     "Secondary settings for request desktop site on Android.";
 const char kRequestDesktopSiteAdditionsDescription[] =
@@ -4262,12 +4229,6 @@
     "Have Read Anything use a local machine learning model for web page"
     "distillation.";
 
-const char kEnableUserCloudSigninRestrictionPolicyName[] =
-    "Cloud User level Signin Restrictions Policy";
-const char kEnableUserCloudSigninRestrictionPolicyDescription[] =
-    "Enable the ManagedAccountsSigninRestrictions policy to be set at a cloud "
-    "user level";
-
 const char kEnableWebHidOnExtensionServiceWorkerName[] =
     "Enable WebHID on extension service workers";
 const char kEnableWebHidOnExtensionServiceWorkerDescription[] =
@@ -4718,6 +4679,12 @@
     "Feature to enable the Jelly design in Personalization App. Requires "
     "jelly-colors flag to be enabled.";
 
+const char kAmbientModeManagedScreensaverName[] =
+    "Enables the managed screensaver";
+const char kAmbientModeManagedScreensaverDescription[] =
+    "Enables a managed screensaver that is controlled by admin through "
+    "enterprise settings.";
+
 const char kAmbientModeThrottleAnimationName[] =
     "Throttle the frame rate of Lottie animations in ambient mode";
 const char kAmbientModeThrottleAnimationDescription[] =
@@ -4958,6 +4925,11 @@
     "Enables the ability to show clicks and keys during video recordings to "
     "enhance demo experience.";
 
+const char kCaptureModeGifRecordingName[] =
+    "Enable GIF recording in screen capture";
+const char kCaptureModeGifRecordingDescription[] =
+    "Enables the ability to record the screen into animated GIFs";
+
 const char kCalendarModelDebugModeName[] = "Monthly Calendar Model Debug Mode";
 const char kCalendarModelDebugModeDescription[] =
     "Debug mode for Monthly Calendar Model. This helps a lot in diagnosing any "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 962494e..380cd4b3 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -395,9 +395,6 @@
 extern const char kClientStorageAccessContextAuditingName[];
 extern const char kClientStorageAccessContextAuditingDescription[];
 
-extern const char kClosedTabCacheName[];
-extern const char kClosedTabCacheDescription[];
-
 extern const char kClearCrossSiteCrossBrowsingContextGroupWindowNameName[];
 extern const char
     kClearCrossSiteCrossBrowsingContextGroupWindowNameDescription[];
@@ -1482,9 +1479,6 @@
 extern const char kRecordWebAppDebugInfoName[];
 extern const char kRecordWebAppDebugInfoDescription[];
 
-extern const char kReduceUserAgentName[];
-extern const char kReduceUserAgentDescription[];
-
 extern const char kRestrictGamepadAccessName[];
 extern const char kRestrictGamepadAccessDescription[];
 
@@ -1948,12 +1942,6 @@
 extern const char kReduceAcceptLanguageName[];
 extern const char kReduceAcceptLanguageDescription[];
 
-extern const char kReduceUserAgentMinorVersionName[];
-extern const char kReduceUserAgentMinorVersionDescription[];
-
-extern const char kReduceUserAgentPlatformOsCpuName[];
-extern const char kReduceUserAgentPlatformOsCpuDescription[];
-
 extern const char kSkipServiceWorkerFetchHandlerName[];
 extern const char kSkipServiceWorkerFetchHandlerDescription[];
 
@@ -2251,9 +2239,6 @@
 extern const char kRelatedSearchesName[];
 extern const char kRelatedSearchesDescription[];
 
-extern const char kRelatedSearchesUiName[];
-extern const char kRelatedSearchesUiDescription[];
-
 extern const char kRequestDesktopSiteAdditionsName[];
 extern const char kRequestDesktopSiteAdditionsDescription[];
 
@@ -2436,9 +2421,6 @@
 extern const char kReadAnythingWithScreen2xName[];
 extern const char kReadAnythingWithScreen2xDescription[];
 
-extern const char kEnableUserCloudSigninRestrictionPolicyName[];
-extern const char kEnableUserCloudSigninRestrictionPolicyDescription[];
-
 extern const char kEnableWebHidOnExtensionServiceWorkerName[];
 extern const char kEnableWebHidOnExtensionServiceWorkerDescription[];
 
@@ -2707,6 +2689,9 @@
 extern const char kAlwaysEnableHdcpType0[];
 extern const char kAlwaysEnableHdcpType1[];
 
+extern const char kAmbientModeManagedScreensaverName[];
+extern const char kAmbientModeManagedScreensaverDescription[];
+
 extern const char kAmbientModeThrottleAnimationName[];
 extern const char kAmbientModeThrottleAnimationDescription[];
 
@@ -2845,6 +2830,9 @@
 extern const char kCaptureModeDemoToolsName[];
 extern const char kCaptureModeDemoToolsDescription[];
 
+extern const char kCaptureModeGifRecordingName[];
+extern const char kCaptureModeGifRecordingDescription[];
+
 extern const char kDesks16Name[];
 extern const char kDesks16Description[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index afee33a..8a9a5bb 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -224,6 +224,7 @@
     &kContextualSearchSuppressShortView,
     &kContextualSearchThinWebViewImplementation,
     &kDeferKeepScreenOnDuringGesture,
+    &kDeferNotifyInMotion,
     &kExperimentsForAgsa,
     &kExploreSites,
     &kFocusOmniboxInIncognitoTabIntents,
@@ -257,7 +258,6 @@
     &kRecordSuppressionMetrics,
     &kReengagementNotification,
     &kRelatedSearches,
-    &kRelatedSearchesUi,
     &kReportParentalControlSitesChild,
     &kRequestDesktopSiteDefaults,
     &kRequestDesktopSiteDefaultsControl,
@@ -705,6 +705,10 @@
              "DeferKeepScreenOnDuringGesture",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kDeferNotifyInMotion,
+             "DeferNotifyInMotion",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kDownloadAutoResumptionThrottling,
              "DownloadAutoResumptionThrottling",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -829,10 +833,6 @@
              "RelatedSearches",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kRelatedSearchesUi,
-             "RelatedSearchesUi",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kReportParentalControlSitesChild,
              "ReportParentalControlSitesChild",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 53d33e8..92e01a54 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -82,6 +82,7 @@
 BASE_DECLARE_FEATURE(kContextualSearchSuppressShortView);
 BASE_DECLARE_FEATURE(kContextualSearchThinWebViewImplementation);
 BASE_DECLARE_FEATURE(kDeferKeepScreenOnDuringGesture);
+BASE_DECLARE_FEATURE(kDeferNotifyInMotion);
 BASE_DECLARE_FEATURE(kDontPrefetchLibraries);
 BASE_DECLARE_FEATURE(kDownloadAutoResumptionThrottling);
 BASE_DECLARE_FEATURE(kDownloadHomeForExternalApp);
@@ -121,7 +122,6 @@
 BASE_DECLARE_FEATURE(kReaderModeInCCT);
 BASE_DECLARE_FEATURE(kRecordSuppressionMetrics);
 BASE_DECLARE_FEATURE(kRelatedSearches);
-BASE_DECLARE_FEATURE(kRelatedSearchesUi);
 BASE_DECLARE_FEATURE(kReportParentalControlSitesChild);
 BASE_DECLARE_FEATURE(kRequestDesktopSiteDefaults);
 BASE_DECLARE_FEATURE(kRequestDesktopSiteDefaultsControl);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 538c3c6..be2470d 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -291,6 +291,7 @@
             "DarkenWebsitesCheckboxInThemesSetting";
     public static final String DEFER_KEEP_SCREEN_ON_DURING_GESTURE =
             "DeferKeepScreenOnDuringGesture";
+    public static final String DEFER_NOTIFY_IN_MOTION = "DeferNotifyInMotion";
     public static final String DETAILED_LANGUAGE_SETTINGS = "DetailedLanguageSettings";
     public static final String DISCO_FEED_ENDPOINT = "DiscoFeedEndpoint";
     public static final String DNS_OVER_HTTPS = "DnsOverHttps";
@@ -442,7 +443,6 @@
     public static final String RECOVER_FROM_NEVER_SAVE_ANDROID = "RecoverFromNeverSaveAndroid";
     public static final String REENGAGEMENT_NOTIFICATION = "ReengagementNotification";
     public static final String RELATED_SEARCHES = "RelatedSearches";
-    public static final String RELATED_SEARCHES_UI = "RelatedSearchesUi";
     public static final String REPORT_PARENTAL_CONTROL_SITES_CHILD =
             "ReportParentalControlSitesChild";
     public static final String REQUEST_DESKTOP_SITE_DEFAULTS = "RequestDesktopSiteDefaults";
diff --git a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc
index a886e0c8..5eceb62 100644
--- a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc
+++ b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/lacros/lacros_file_system_provider.h"
 #include "chrome/browser/lacros/lacros_memory_pressure_evaluator.h"
 #include "chrome/browser/lacros/launcher_search/search_controller_lacros.h"
+#include "chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.h"
 #include "chrome/browser/lacros/net/network_change_manager_bridge.h"
 #include "chrome/browser/lacros/screen_orientation_delegate_lacros.h"
 #include "chrome/browser/lacros/standalone_browser_test_controller.h"
@@ -212,6 +213,11 @@
 
   smart_reader_client_ =
       std::make_unique<smart_reader::SmartReaderClientImpl>();
+
+  if (chromeos::BrowserParamsProxy::Get()->IsWindowLayoutMenuEnabled()) {
+    multitask_menu_nudge_delegate_ =
+        std::make_unique<MultitaskMenuNudgeDelegateLacros>();
+  }
 }
 
 void ChromeBrowserMainExtraPartsLacros::PostProfileInit(
diff --git a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h
index 8d2cbe87..719e4d5 100644
--- a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h
+++ b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h
@@ -34,6 +34,7 @@
 class UiMetricRecorderLacros;
 class VpnExtensionTrackerLacros;
 class WebAuthnRequestRegistrarLacros;
+class MultitaskMenuNudgeDelegateLacros;
 
 namespace arc {
 class ArcIconCacheDelegateProvider;
@@ -185,6 +186,10 @@
 
   // Controls sync-related Crosapi clients.
   SyncCrosapiManagerLacros sync_crosapi_manager_;
+
+  // Handles getting and setting multitask menu nudge related prefs from ash.
+  std::unique_ptr<MultitaskMenuNudgeDelegateLacros>
+      multitask_menu_nudge_delegate_;
 };
 
 #endif  // CHROME_BROWSER_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_
diff --git a/chrome/browser/lacros/desk_template_client_lacros.cc b/chrome/browser/lacros/desk_template_client_lacros.cc
index a638ebd..0d2870c 100644
--- a/chrome/browser/lacros/desk_template_client_lacros.cc
+++ b/chrome/browser/lacros/desk_template_client_lacros.cc
@@ -61,20 +61,21 @@
   return true;
 }
 
-// Creates a callback for when a favicon image is retrieved which creates a
-// standard icon image and then calls `callback` with the standardized image.
-base::OnceCallback<void(const favicon_base::FaviconImageResult&)>
-CreateFaviconResultCallback(
-    base::OnceCallback<void(const gfx::ImageSkia&)> callback) {
-  return base::BindOnce(
-      [](base::OnceCallback<void(const gfx::ImageSkia&)> image_skia_callback,
-         const favicon_base::FaviconImageResult& result) {
-        auto image = result.image.AsImageSkia();
-        image.EnsureRepsForSupportedScales();
-        std::move(image_skia_callback)
-            .Run(apps::CreateStandardIconImage(image));
-      },
-      std::move(callback));
+// Creates a standard icon image via `result`, and then calls `callback` with
+// the standardized image.
+void ImageResultToImageSkia(
+    base::OnceCallback<void(const gfx::ImageSkia&)> callback,
+    const favicon_base::FaviconRawBitmapResult& result) {
+  if (!result.is_valid()) {
+    std::move(callback).Run(gfx::ImageSkia());
+    return;
+  }
+
+  auto image = gfx::Image::CreateFrom1xPNGBytes(result.bitmap_data->front(),
+                                                result.bitmap_data->size())
+                   .AsImageSkia();
+  image.EnsureRepsForSupportedScales();
+  std::move(callback).Run(apps::CreateStandardIconImage(image));
 }
 
 void AddTabGroupToBrowser(TabStripModel* browser_tab_model,
@@ -247,6 +248,9 @@
           ProfileManager::GetActiveUserProfile(),
           ServiceAccessType::EXPLICIT_ACCESS);
 
-  favicon_service->GetFaviconImageForPageURL(
-      url, CreateFaviconResultCallback(std::move(callback)), &task_tracker_);
+  favicon_service->GetRawFaviconForPageURL(
+      url, {favicon_base::IconType::kFavicon}, 0,
+      /*fallback_to_host=*/false,
+      base::BindOnce(&ImageResultToImageSkia, std::move(callback)),
+      &task_tracker_);
 }
diff --git a/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.cc b/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.cc
new file mode 100644
index 0000000..71e4ded
--- /dev/null
+++ b/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.cc
@@ -0,0 +1,122 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.h"
+
+#include "base/barrier_callback.h"
+#include "base/json/values_util.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "multitask_menu_nudge_delegate_lacros.h"
+
+namespace {
+
+// If we can't get the pref, run the callback with the max nudge show count
+// value so that no nudge gets shown.
+// TODO(b/267787811): The callback should instead be updated to provide a
+// success parameter.
+constexpr int kMaxNudgeShowCount = 3;
+
+chromeos::LacrosService* GetLacrosService() {
+  auto* lacros_service = chromeos::LacrosService::Get();
+  if (lacros_service && lacros_service->IsAvailable<crosapi::mojom::Prefs>()) {
+    return lacros_service;
+  }
+  return nullptr;
+}
+
+}  // namespace
+
+MultitaskMenuNudgeDelegateLacros::MultitaskMenuNudgeDelegateLacros() = default;
+
+MultitaskMenuNudgeDelegateLacros::~MultitaskMenuNudgeDelegateLacros() = default;
+
+int MultitaskMenuNudgeDelegateLacros::GetTabletNudgeYOffset() const {
+  // Tablet nudge is handled by ash.
+  NOTREACHED();
+  return 0;
+}
+
+void MultitaskMenuNudgeDelegateLacros::GetNudgePreferences(
+    bool tablet_mode,
+    GetPreferencesCallback callback) {
+  // These prefs should be read from ash, as they are also used by frames
+  // created and maintained in ash.
+  auto* lacros_service = GetLacrosService();
+  if (!lacros_service) {
+    std::move(callback).Run(/*tablet_mode=*/false, kMaxNudgeShowCount,
+                            base::Time());
+    return;
+  }
+
+  auto barrier = base::BarrierCallback<PrefPair>(
+      /*num_callbacks=*/2u, /*done_callback=*/base::BindOnce(
+          &MultitaskMenuNudgeDelegateLacros::OnGotAllPreferences,
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+
+  lacros_service->GetRemote<crosapi::mojom::Prefs>()->GetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount,
+      base::BindOnce(
+          &MultitaskMenuNudgeDelegateLacros::OnGetPreference,
+          weak_ptr_factory_.GetWeakPtr(), barrier,
+          crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount));
+  lacros_service->GetRemote<crosapi::mojom::Prefs>()->GetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown,
+      base::BindOnce(
+          &MultitaskMenuNudgeDelegateLacros::OnGetPreference,
+          weak_ptr_factory_.GetWeakPtr(), barrier,
+          crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown));
+}
+
+void MultitaskMenuNudgeDelegateLacros::SetNudgePreferences(bool tablet_mode,
+                                                           int count,
+                                                           base::Time time) {
+  auto* lacros_service = GetLacrosService();
+  if (!lacros_service) {
+    return;
+  }
+
+  lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount,
+      base::Value(count), base::DoNothing());
+  lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown,
+      base::TimeToValue(time), base::DoNothing());
+}
+
+void MultitaskMenuNudgeDelegateLacros::OnGetPreference(
+    base::OnceCallback<void(PrefPair)> callback,
+    crosapi::mojom::PrefPath pref_path,
+    absl::optional<base::Value> value) {
+  // If `value` is empty just pass a default `base::Value`; the other callback
+  // (`OnGotAllPreferences()`) function will handle it properly.
+  PrefPair pref_pair{pref_path, value ? std::move(*value) : base::Value()};
+  std::move(callback).Run(std::move(pref_pair));
+}
+
+void MultitaskMenuNudgeDelegateLacros::OnGotAllPreferences(
+    GetPreferencesCallback callback,
+    std::vector<PrefPair> pref_values) {
+  DCHECK_EQ(2u, pref_values.size());
+
+  // The values in the array could be in any order. Parse them into the
+  // `shown_count` and `last_shown_time`.
+  absl::optional<int> shown_count;
+  absl::optional<base::Time> last_shown_time;
+  for (const auto& pair : pref_values) {
+    if (pair.first ==
+        crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount) {
+      shown_count = pair.second.GetIfInt().value_or(kMaxNudgeShowCount);
+    } else {
+      DCHECK_EQ(
+          pair.first,
+          crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown);
+      last_shown_time = base::ValueToTime(pair.second).value_or(base::Time());
+    }
+  }
+
+  DCHECK(shown_count.has_value());
+  DCHECK(last_shown_time.has_value());
+  std::move(callback).Run(/*tablet_mode=*/false, *shown_count,
+                          *last_shown_time);
+}
diff --git a/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.h b/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.h
new file mode 100644
index 0000000..ded06f37
--- /dev/null
+++ b/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.h
@@ -0,0 +1,55 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_LACROS_MULTITASK_MENU_NUDGE_DELEGATE_LACROS_
+#define CHROME_BROWSER_LACROS_MULTITASK_MENU_NUDGE_DELEGATE_LACROS_
+
+#include "base/values.h"
+#include "chromeos/crosapi/mojom/prefs.mojom.h"
+#include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h"
+
+// Lacros implementation of the nudge controller delegate that lets us get and
+// set pref values from the ash active profile via mojo.
+class MultitaskMenuNudgeDelegateLacros
+    : public chromeos::MultitaskMenuNudgeController::Delegate {
+ public:
+  using GetPreferencesCallback =
+      chromeos::MultitaskMenuNudgeController::GetPreferencesCallback;
+
+  static constexpr int kTabletNudgeAdditionalYOffset = 6;
+
+  MultitaskMenuNudgeDelegateLacros();
+  MultitaskMenuNudgeDelegateLacros(const MultitaskMenuNudgeDelegateLacros&) =
+      delete;
+  MultitaskMenuNudgeDelegateLacros& operator=(
+      const MultitaskMenuNudgeDelegateLacros&) = delete;
+  ~MultitaskMenuNudgeDelegateLacros() override;
+
+  // chromeos::MultitaskMenuNudgeController::Delegate:
+  int GetTabletNudgeYOffset() const override;
+  void GetNudgePreferences(bool tablet_mode,
+                           GetPreferencesCallback callback) override;
+  void SetNudgePreferences(bool tablet_mode,
+                           int count,
+                           base::Time time) override;
+
+ private:
+  using PrefPair = std::pair<crosapi::mojom::PrefPath, base::Value>;
+
+  // Callback ran when we got either pref from the pref service. Runs
+  // `callback`, which is part of a barrier callback.
+  void OnGetPreference(base::OnceCallback<void(PrefPair)> callback,
+                       crosapi::mojom::PrefPath pref_path,
+                       absl::optional<base::Value> value);
+
+  // Callback ran when we got both our prefs from the pref service. Parses the
+  // values and then uses `callback` to send them to the prefs requester.
+  void OnGotAllPreferences(GetPreferencesCallback callback,
+                           std::vector<PrefPair> pref_values);
+
+  base::WeakPtrFactory<MultitaskMenuNudgeDelegateLacros> weak_ptr_factory_{
+      this};
+};
+
+#endif  // CHROME_BROWSER_LACROS_MULTITASK_MENU_NUDGE_DELEGATE_LACROS_
diff --git a/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros_browsertest.cc b/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros_browsertest.cc
new file mode 100644
index 0000000..03b9df8
--- /dev/null
+++ b/chrome/browser/lacros/multitask_menu_nudge_delegate_lacros_browsertest.cc
@@ -0,0 +1,55 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/lacros/multitask_menu_nudge_delegate_lacros.h"
+
+#include "base/json/values_util.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/crosapi/mojom/prefs.mojom-test-utils.h"
+#include "chromeos/crosapi/mojom/prefs.mojom.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "content/public/test/browser_test.h"
+
+using MultitaskMenuNudgeDelegateLacrosBrowserTest = InProcessBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(MultitaskMenuNudgeDelegateLacrosBrowserTest, Basics) {
+  auto* lacros_service = chromeos::LacrosService::Get();
+  ASSERT_TRUE(lacros_service);
+  ASSERT_TRUE(lacros_service->IsAvailable<crosapi::mojom::Prefs>());
+
+  crosapi::mojom::PrefsAsyncWaiter async_waiter(
+      chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>().get());
+
+  absl::optional<base::Value> int_value;
+  async_waiter.GetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount,
+      &int_value);
+
+  // If the pref cannot be fetched, the ash version may be too old.
+  if (!int_value.has_value()) {
+    GTEST_SKIP() << "Skipping as the nudge prefs are not available in the "
+                    "current version of Ash";
+  }
+
+  base::Time expected_time = base::Time();
+  async_waiter.SetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount,
+      base::Value(2));
+  async_waiter.SetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown,
+      base::TimeToValue(expected_time));
+
+  absl::optional<base::Value> time_value;
+  async_waiter.GetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellShownCount,
+      &int_value);
+  async_waiter.GetPref(
+      crosapi::mojom::PrefPath::kMultitaskMenuNudgeClamshellLastShown,
+      &time_value);
+  ASSERT_TRUE(int_value.has_value());
+  EXPECT_EQ(2, int_value.value());
+
+  ASSERT_TRUE(time_value.has_value());
+  EXPECT_EQ(expected_time, base::ValueToTime(*time_value).value());
+}
diff --git a/chrome/browser/media/webrtc/get_display_media_set_browsertest.cc b/chrome/browser/media/webrtc/get_display_media_set_browsertest.cc
index 17153740..a86d804 100644
--- a/chrome/browser/media/webrtc/get_display_media_set_browsertest.cc
+++ b/chrome/browser/media/webrtc/get_display_media_set_browsertest.cc
@@ -138,7 +138,7 @@
       // Each entry in this comma separated list corresponds to a screen
       // specification following the format defined in
       // |ManagedDisplayInfo::CreateFromSpec|.
-      // The used specficiation simulatoes screens with resolution 800x800
+      // The used specification simulates screens with resolution 800x800
       // at the host coordinates (screen_index * 800, 0).
       screens << screen_index * 640 << "+0-640x480,";
     }
diff --git a/chrome/browser/metrics/metrics_reporting_state.h b/chrome/browser/metrics/metrics_reporting_state.h
index b486801..60aeb036 100644
--- a/chrome/browser/metrics/metrics_reporting_state.h
+++ b/chrome/browser/metrics/metrics_reporting_state.h
@@ -16,10 +16,14 @@
 // TODO(crbug.com/1296618): Make all call sites pass an appropriate value, and
 // remove |kUnknown|. Right now, |kUnknown| is used as a placeholder value while
 // call sites are being migrated.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.metrics
 enum class ChangeMetricsReportingStateCalledFrom {
   kUnknown,
   kUiSettings,
 
+  // The user opted out of metrics reporting in the First Run Experience.
+  kUiFirstRun,
+
   // Called from Chrome OS settings change. Chrome OS manages settings
   // externally and metrics service listens for changes.
   kCrosMetricsSettingsChange,
diff --git a/chrome/browser/metrics/metrics_reporting_state_browsertest.cc b/chrome/browser/metrics/metrics_reporting_state_browsertest.cc
index 345af57b..c350a7a3 100644
--- a/chrome/browser/metrics/metrics_reporting_state_browsertest.cc
+++ b/chrome/browser/metrics/metrics_reporting_state_browsertest.cc
@@ -311,6 +311,7 @@
     testing::ValuesIn<ChangeMetricsReportingStateCalledFrom>(
         {ChangeMetricsReportingStateCalledFrom::kUnknown,
          ChangeMetricsReportingStateCalledFrom::kUiSettings,
+         ChangeMetricsReportingStateCalledFrom::kUiFirstRun,
          ChangeMetricsReportingStateCalledFrom::kCrosMetricsSettingsChange}));
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
index 718068c..db0772cb 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
@@ -124,9 +124,8 @@
   using memory_instrumentation::mojom::VmRegion;
 
   return memory_instrumentation::mojom::OSMemDump::New(
-      resident_set_kb, resident_set_kb /* peak_resident_set_kb */,
-      true /* is_peak_rss_resettable */, private_footprint_kb,
-      shared_footprint_kb
+      resident_set_kb, /*peak_resident_set_kb=*/resident_set_kb,
+      /*is_peak_rss_resettable=*/true, private_footprint_kb, shared_footprint_kb
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
       ,
       private_swap_footprint_kb
@@ -134,6 +133,21 @@
   );
 }
 
+OSMemDumpPtr GetFakeOSMemDump(MetricMap& metrics_mb) {
+  return GetFakeOSMemDump(
+      /*resident_set_kb=*/GetResidentValue(metrics_mb) * 1024,
+      /*private_footprint_kb=*/metrics_mb["PrivateMemoryFootprint"] * 1024,
+      /*shared_footprint_kb=*/metrics_mb["SharedMemoryFootprint"] * 1024
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
+      // accessing PrivateSwapFootprint on other OSes will
+      // modify metrics_mb to create the value, which leads
+      // to expectation failures.
+      ,
+      /*private_swap_footprint_kb=*/metrics_mb["PrivateSwapFootprint"] * 1024
+#endif
+  );
+}
+
 constexpr uint64_t kGpuSharedImagesSizeMB = 32;
 constexpr uint64_t kGpuSkiaGpuResourcesMB = 87;
 constexpr uint64_t kGpuCommandBufferMB = 240;
@@ -155,19 +169,7 @@
                          kGpuSharedImagesSizeMB * 1024 * 1024);
   SetAllocatorDumpMetric(pmd, "skia/gpu_resources", "effective_size",
                          kGpuSkiaGpuResourcesMB * 1024 * 1024);
-  OSMemDumpPtr os_dump =
-      GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
-                       metrics_mb["PrivateMemoryFootprint"] * 1024,
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-                       // accessing PrivateSwapFootprint on other OSes will
-                       // modify metrics_mb to create the value, which leads to
-                       // expectation failures.
-                       metrics_mb["SharedMemoryFootprint"] * 1024,
-                       metrics_mb["PrivateSwapFootprint"] * 1024
-#else
-                       metrics_mb["SharedMemoryFootprint"] * 1024
-#endif
-      );
+  OSMemDumpPtr os_dump = GetFakeOSMemDump(metrics_mb);
   pmd->os_dump = std::move(os_dump);
   global_dump->process_dumps.push_back(std::move(pmd));
 }
@@ -316,19 +318,7 @@
       metrics_mb_or_count["PartitionAlloc.Partitions.ArrayBuffer"] * 1024 *
           1024);
 
-  OSMemDumpPtr os_dump =
-      GetFakeOSMemDump(GetResidentValue(metrics_mb_or_count) * 1024,
-                       metrics_mb_or_count["PrivateMemoryFootprint"] * 1024,
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-                       // accessing PrivateSwapFootprint on other OSes will
-                       // modify metrics_mb_or_count to create the value, which
-                       // leads to expectation failures.
-                       metrics_mb_or_count["SharedMemoryFootprint"] * 1024,
-                       metrics_mb_or_count["PrivateSwapFootprint"] * 1024
-#else
-                       metrics_mb_or_count["SharedMemoryFootprint"] * 1024
-#endif
-      );
+  OSMemDumpPtr os_dump = GetFakeOSMemDump(metrics_mb_or_count);
   pmd->os_dump = std::move(os_dump);
   pmd->pid = pid;
   global_dump->process_dumps.push_back(std::move(pmd));
@@ -404,19 +394,7 @@
                          kGpuSharedImagesSizeMB * 1024 * 1024);
   SetAllocatorDumpMetric(pmd, "skia/gpu_resources", "effective_size",
                          kGpuSkiaGpuResourcesMB * 1024 * 1024);
-  OSMemDumpPtr os_dump =
-      GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
-                       metrics_mb["PrivateMemoryFootprint"] * 1024,
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-                       // accessing PrivateSwapFootprint on other OSes will
-                       // modify metrics_mb to create the value, which leads to
-                       // expectation failures.
-                       metrics_mb["SharedMemoryFootprint"] * 1024,
-                       metrics_mb["PrivateSwapFootprint"] * 1024
-#else
-                       metrics_mb["SharedMemoryFootprint"] * 1024
-#endif
-      );
+  OSMemDumpPtr os_dump = GetFakeOSMemDump(metrics_mb);
   pmd->os_dump = std::move(os_dump);
   global_dump->process_dumps.push_back(std::move(pmd));
 }
@@ -433,26 +411,18 @@
   });
 }
 
-void PopulateAudioServiceMetrics(GlobalMemoryDumpPtr& global_dump,
-                                 MetricMap& metrics_mb) {
-  ProcessMemoryDumpPtr pmd(
-      memory_instrumentation::mojom::ProcessMemoryDump::New());
+void PopulateUtilityMetrics(GlobalMemoryDumpPtr& global_dump,
+                            MetricMap& metrics_mb,
+                            const absl::optional<std::string>& service_name) {
+  auto pmd(memory_instrumentation::mojom::ProcessMemoryDump::New());
   pmd->process_type = ProcessType::UTILITY;
+  if (service_name.has_value()) {
+    pmd->service_name = service_name.value();
+  }
+
   SetAllocatorDumpMetric(pmd, "malloc", "effective_size",
                          metrics_mb["Malloc"] * 1024 * 1024);
-  OSMemDumpPtr os_dump =
-      GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
-                       metrics_mb["PrivateMemoryFootprint"] * 1024,
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-                       // accessing PrivateSwapFootprint on other OSes will
-                       // modify metrics_mb to create the value, which leads to
-                       // expectation failures.
-                       metrics_mb["SharedMemoryFootprint"] * 1024,
-                       metrics_mb["PrivateSwapFootprint"] * 1024
-#else
-                       metrics_mb["SharedMemoryFootprint"] * 1024
-#endif
-      );
+  OSMemDumpPtr os_dump = GetFakeOSMemDump(metrics_mb);
   pmd->os_dump = std::move(os_dump);
   global_dump->process_dumps.push_back(std::move(pmd));
 }
@@ -468,30 +438,6 @@
   });
 }
 
-void PopulatePaintPreviewCompositorMetrics(GlobalMemoryDumpPtr& global_dump,
-                                           MetricMap& metrics_mb) {
-  auto process_memory_dump =
-      memory_instrumentation::mojom::ProcessMemoryDump::New();
-  process_memory_dump->service_name =
-      paint_preview::mojom::PaintPreviewCompositorCollection::Name_;
-  ProcessMemoryDumpPtr pmd(std::move(process_memory_dump));
-  pmd->process_type = ProcessType::UTILITY;
-  OSMemDumpPtr os_dump =
-      GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
-                       metrics_mb["PrivateMemoryFootprint"] * 1024,
-                       metrics_mb["SharedMemoryFootprint"] * 1024
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
-                       // accessing PrivateSwapFootprint on other OSes will
-                       // modify metrics_mb to create the value, which leads to
-                       // expectation failures.
-                       ,
-                       metrics_mb["PrivateSwapFootprint"] * 1024
-#endif
-      );
-  pmd->os_dump = std::move(os_dump);
-  global_dump->process_dumps.push_back(std::move(pmd));
-}
-
 MetricMap GetExpectedPaintPreviewCompositorMetrics() {
   return MetricMap({
     {"ProcessType", static_cast<int64_t>(ProcessType::UTILITY)},
@@ -508,7 +454,8 @@
                      MetricMap& metrics_mb) {
   switch (ptype) {
     case HistogramProcessType::kAudioService:
-      PopulateAudioServiceMetrics(global_dump, metrics_mb);
+      PopulateUtilityMetrics(global_dump, metrics_mb,
+                             /*service_name=*/absl::nullopt);
       return;
     case HistogramProcessType::kBrowser:
       PopulateBrowserMetrics(global_dump, metrics_mb);
@@ -517,7 +464,9 @@
       PopulateGpuMetrics(global_dump, metrics_mb);
       return;
     case HistogramProcessType::kPaintPreviewCompositor:
-      PopulatePaintPreviewCompositorMetrics(global_dump, metrics_mb);
+      PopulateUtilityMetrics(
+          global_dump, metrics_mb,
+          paint_preview::mojom::PaintPreviewCompositorCollection::Name_);
       return;
     case HistogramProcessType::kRenderer:
       PopulateRendererMetrics(global_dump, metrics_mb, 101);
@@ -619,14 +568,14 @@
 class ProcessMemoryMetricsEmitterTest
     : public testing::TestWithParam<HistogramProcessType> {
  public:
-  ProcessMemoryMetricsEmitterTest() {}
+  ProcessMemoryMetricsEmitterTest() = default;
 
   ProcessMemoryMetricsEmitterTest(const ProcessMemoryMetricsEmitterTest&) =
       delete;
   ProcessMemoryMetricsEmitterTest& operator=(
       const ProcessMemoryMetricsEmitterTest&) = delete;
 
-  ~ProcessMemoryMetricsEmitterTest() override {}
+  ~ProcessMemoryMetricsEmitterTest() override = default;
 
  protected:
   void CheckMemoryUkmEntryMetrics(const std::vector<MetricMap>& expected,
diff --git a/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc b/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
index 40089f6c..43eaec9ee 100644
--- a/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
+++ b/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
@@ -59,7 +59,7 @@
   }
 
   if (base::FeatureList::IsEnabled(ntp_features::kNtpHistoryClustersModule)) {
-    details.emplace_back("history-clusters",
+    details.emplace_back("history_clusters",
                          IDS_HISTORY_CLUSTERS_JOURNEYS_TAB_LABEL);
   }
 
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
index 9424428b..ee0dc385 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
@@ -125,7 +125,10 @@
 
 void ForegroundDurationUKMObserver::DidActivatePrerenderedPage(
     content::NavigationHandle* navigation_handle) {
-  DCHECK(GetDelegate().WasPrerenderedThenActivatedInForeground());
-  last_time_shown_ = base::TimeTicks::Now();
-  currently_in_foreground_ = true;
+  if (GetDelegate().WasPrerenderedThenActivatedInForeground()) {
+    last_time_shown_ = base::TimeTicks::Now();
+    currently_in_foreground_ = true;
+  } else {
+    currently_in_foreground_ = false;
+  }
 }
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
index 58fb3939..1c6049d 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
@@ -44,7 +44,10 @@
       content::NavigationHandle* navigation_handle) override;
 
  private:
+  // True when the visibility of WebContents is Visibility::VISIBLE (not
+  // OCCLUDED or HIDDEN).
   bool currently_in_foreground_ = false;
+
   base::TimeTicks last_time_shown_;
   page_load_metrics::mojom::InputTimingPtr last_page_input_timing_;
   void RecordUkmIfInForeground(base::TimeTicks end_time);
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc
index 3250840..1cedb7e1 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc
@@ -109,25 +109,56 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ForegroundDurationUKMObserverBrowserTest,
-                       PrerenderSimple) {
+                       PrerenderActivationInForeground) {
   StartHttpsServer(net::EmbeddedTestServer::CERT_OK);
   GURL empty = https_test_server()->GetURL("/empty.html");
   GURL simple = https_test_server()->GetURL("/simple.html");
   prerender_helper().NavigatePrimaryPage(empty);
   int host_id = prerender_helper().AddPrerender(simple);
   prerender_helper().WaitForPrerenderLoadCompletion(host_id);
+
   ExpectMetricCountForUrl(simple, "ForegroundDuration", 0);
   ExpectMetricCountForUrl(simple, "ForegroundNumInputEvents", 0);
   ExpectMetricCountForUrl(simple, "ForegroundTotalInputDelay", 0);
   ExpectMetricCountForUrl(simple, "ForegroundTotalAdjustedInputDelay", 0);
   prerender_helper().NavigatePrimaryPage(simple);
   CloseAllTabs();
+
+  // The page was activated in foreground. The metrics should be recorded.
   ExpectMetricCountForUrl(simple, "ForegroundDuration", 1);
   ExpectMetricCountForUrl(simple, "ForegroundNumInputEvents", 1);
   ExpectMetricCountForUrl(simple, "ForegroundTotalInputDelay", 1);
   ExpectMetricCountForUrl(simple, "ForegroundTotalAdjustedInputDelay", 1);
 }
 
+IN_PROC_BROWSER_TEST_F(ForegroundDurationUKMObserverBrowserTest,
+                       PrerenderActivationInBackground) {
+  StartHttpsServer(net::EmbeddedTestServer::CERT_OK);
+  GURL empty = https_test_server()->GetURL("/empty.html");
+  GURL simple = https_test_server()->GetURL("/simple.html");
+  prerender_helper().NavigatePrimaryPage(empty);
+  int host_id = prerender_helper().AddPrerender(simple);
+  prerender_helper().WaitForPrerenderLoadCompletion(host_id);
+
+  // Make the initiator page occluded. This will be treated as a background
+  // page. Note that we cannot make the initiator page hidden here as a hidden
+  // page cannot activate a prerendered page.
+  web_contents()->WasOccluded();
+
+  ExpectMetricCountForUrl(simple, "ForegroundDuration", 0);
+  ExpectMetricCountForUrl(simple, "ForegroundNumInputEvents", 0);
+  ExpectMetricCountForUrl(simple, "ForegroundTotalInputDelay", 0);
+  ExpectMetricCountForUrl(simple, "ForegroundTotalAdjustedInputDelay", 0);
+  prerender_helper().NavigatePrimaryPage(simple);
+  CloseAllTabs();
+
+  // The page was activated in background. The metrics should not be recorded.
+  ExpectMetricCountForUrl(simple, "ForegroundDuration", 0);
+  ExpectMetricCountForUrl(simple, "ForegroundNumInputEvents", 0);
+  ExpectMetricCountForUrl(simple, "ForegroundTotalInputDelay", 0);
+  ExpectMetricCountForUrl(simple, "ForegroundTotalAdjustedInputDelay", 0);
+}
+
 IN_PROC_BROWSER_TEST_F(ForegroundDurationUKMObserverBrowserTest, TabSwitching) {
   StartHttpsServer(net::EmbeddedTestServer::CERT_OK);
   GURL url1 = https_test_server()->GetURL("/simple.html");
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.cc b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
index e4cd392..731cef75 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
@@ -68,8 +68,9 @@
   // Initialize the result map in one shot for time complexity O(n * log(n)).
   NodeRssMap::container_type result_container;
   result_container.reserve(candidates.size());
-  for (auto candidate : candidates)
+  for (auto candidate : candidates) {
     result_container.emplace_back(candidate.page_node(), 0);
+  }
   NodeRssMap result(std::move(result_container));
 
   // TODO(crbug/1240994): Use visitor to accumulate the result to avoid
@@ -88,8 +89,9 @@
   for (const ProcessNode* process_node : process_nodes) {
     base::flat_set<const FrameNode*> process_frames =
         process_node->GetFrameNodes();
-    if (!process_frames.size())
+    if (!process_frames.size()) {
       continue;
+    }
     // Get the resident set of the process and split it equally across its
     // frames.
     const uint64_t frame_rss_kb =
@@ -98,8 +100,9 @@
       // Check if the frame belongs to a discardable page, if so update the
       // resident set of the page.
       auto iter = result.find(frame_node->GetPageNode());
-      if (iter == result.end())
+      if (iter == result.end()) {
         continue;
+      }
       iter->second += frame_rss_kb;
     }
   }
@@ -137,12 +140,14 @@
   std::vector<PageNodeSortProxy> candidates;
   for (const auto* page_node : page_nodes) {
     CanUrgentlyDiscardResult can_discard_result = CanUrgentlyDiscard(page_node);
-    if (can_discard_result == CanUrgentlyDiscardResult::kMarked)
+    if (can_discard_result == CanUrgentlyDiscardResult::kMarked) {
       continue;
+    }
     bool is_protected =
         (can_discard_result == CanUrgentlyDiscardResult::kProtected);
-    if (!discard_protected_tabs && is_protected)
+    if (!discard_protected_tabs && is_protected) {
       continue;
+    }
     candidates.emplace_back(page_node, false, is_protected,
                             page_node->GetTimeSinceLastVisibilityChange());
   }
@@ -167,8 +172,9 @@
     uint64_t total_reclaim_kb = 0;
     NodeRssMap page_node_rss_kb = GetPageNodeRssEstimateKb(candidates);
     for (auto& candidate : candidates) {
-      if (total_reclaim_kb >= reclaim_target_kb_value)
+      if (total_reclaim_kb >= reclaim_target_kb_value) {
         break;
+      }
       const PageNode* node = candidate.page_node();
       discard_attempts.emplace_back(node);
       // The node RSS value is updated by ProcessMetricsDecorator periodically.
@@ -222,8 +228,9 @@
 
 void PageDiscardingHelper::OnIsAudibleChanged(const PageNode* page_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!page_node->IsAudible())
+  if (!page_node->IsAudible()) {
     last_change_to_non_audible_time_[page_node] = base::TimeTicks::Now();
+  }
 }
 
 void PageDiscardingHelper::SetNoDiscardPatternsForProfile(
@@ -284,19 +291,23 @@
 PageDiscardingHelper::CanUrgentlyDiscard(
     const PageNode* page_node,
     bool consider_minimum_protection_time) const {
-  if (DiscardAttemptMarker::Get(PageNodeImpl::FromNode(page_node)))
+  if (DiscardAttemptMarker::Get(PageNodeImpl::FromNode(page_node))) {
     return CanUrgentlyDiscardResult::kMarked;
+  }
 
-  if (page_node->IsVisible())
+  if (page_node->IsVisible()) {
     return CanUrgentlyDiscardResult::kProtected;
-  if (page_node->IsAudible())
+  }
+  if (page_node->IsAudible()) {
     return CanUrgentlyDiscardResult::kProtected;
+  }
 
   // Don't discard tabs that have recently played audio.
   auto it = last_change_to_non_audible_time_.find(page_node);
   if (it != last_change_to_non_audible_time_.end()) {
-    if (base::TimeTicks::Now() - it->second < kTabAudioProtectionTime)
+    if (base::TimeTicks::Now() - it->second < kTabAudioProtectionTime) {
       return CanUrgentlyDiscardResult::kProtected;
+    }
   }
 
 #if !BUILDFLAG(IS_CHROMEOS)
@@ -310,24 +321,28 @@
   // Do not discard PDFs as they might contain entry that is not saved and they
   // don't remember their scrolling positions. See crbug.com/547286 and
   // crbug.com/65244.
-  if (page_node->GetContentsMimeType() == "application/pdf")
+  if (page_node->GetContentsMimeType() == "application/pdf") {
     return CanUrgentlyDiscardResult::kProtected;
+  }
 
   // Don't discard tabs that don't have a main frame yet.
   auto* main_frame = page_node->GetMainFrameNode();
-  if (!main_frame)
+  if (!main_frame) {
     return CanUrgentlyDiscardResult::kProtected;
+  }
 
   // Only discard http(s) pages and internal pages to make sure that we don't
   // discard extensions or other PageNode that don't correspond to a tab.
   bool is_web_page_or_internal_page =
       main_frame->GetURL().SchemeIsHTTPOrHTTPS() ||
       main_frame->GetURL().SchemeIs("chrome");
-  if (!is_web_page_or_internal_page)
+  if (!is_web_page_or_internal_page) {
     return CanUrgentlyDiscardResult::kProtected;
+  }
 
-  if (!main_frame->GetURL().is_valid() || main_frame->GetURL().is_empty())
+  if (!main_frame->GetURL().is_valid() || main_frame->GetURL().is_empty()) {
     return CanUrgentlyDiscardResult::kProtected;
+  }
 
   if (IsPageOptedOutOfDiscarding(page_node->GetBrowserContextID(),
                                  main_frame->GetURL())) {
@@ -339,24 +354,33 @@
   // The live state data won't be available if none of these events ever
   // happened on the page.
   if (live_state_data) {
-    if (!live_state_data->IsAutoDiscardable())
+    if (!live_state_data->IsAutoDiscardable()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsCapturingVideo())
+    }
+    if (live_state_data->IsCapturingVideo()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsCapturingAudio())
+    }
+    if (live_state_data->IsCapturingAudio()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsBeingMirrored())
+    }
+    if (live_state_data->IsBeingMirrored()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsCapturingWindow())
+    }
+    if (live_state_data->IsCapturingWindow()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsCapturingDisplay())
+    }
+    if (live_state_data->IsCapturingDisplay()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsConnectedToBluetoothDevice())
+    }
+    if (live_state_data->IsConnectedToBluetoothDevice()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsConnectedToUSBDevice())
+    }
+    if (live_state_data->IsConnectedToUSBDevice()) {
       return CanUrgentlyDiscardResult::kProtected;
-    if (live_state_data->IsActiveTab())
+    }
+    if (live_state_data->IsActiveTab()) {
       return CanUrgentlyDiscardResult::kProtected;
+    }
     if (live_state_data->IsPinnedTab()) {
       return CanUrgentlyDiscardResult::kProtected;
     }
@@ -369,16 +393,18 @@
     }
 #if !BUILDFLAG(IS_CHROMEOS)
     // TODO(sebmarchand): Skip this check if the Entreprise memory limit is set.
-    if (live_state_data->WasDiscarded())
+    if (live_state_data->WasDiscarded()) {
       return CanUrgentlyDiscardResult::kProtected;
-      // TODO(sebmarchand): Consider resetting the |WasDiscarded| value when the
-      // main frame document changes, also remove the DiscardAttemptMarker in
-      // this case.
+    }
+    // TODO(sebmarchand): Consider resetting the |WasDiscarded| value when the
+    // main frame document changes, also remove the DiscardAttemptMarker in
+    // this case.
 #endif
   }
 
-  if (page_node->HadFormInteraction())
+  if (page_node->HadFormInteraction()) {
     return CanUrgentlyDiscardResult::kProtected;
+  }
 
   // TODO(sebmarchand): Do not discard crashed tabs.
 
@@ -407,8 +433,9 @@
 base::Value::Dict PageDiscardingHelper::DescribePageNodeData(
     const PageNode* node) const {
   auto* data = DiscardAttemptMarker::Get(PageNodeImpl::FromNode(node));
-  if (data == nullptr)
+  if (data == nullptr) {
     return base::Value::Dict();
+  }
 
   base::Value::Dict ret;
   ret.Set("has_discard_attempt_marker", base::Value("true"));
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 e0d7724..b6031a6a 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
 #include "chrome/browser/sharing/features.h"
+#include "chrome/browser/sharing_hub/sharing_hub_features.h"
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
@@ -3360,8 +3361,15 @@
 }
 
 bool RenderViewContextMenu::IsQRCodeGeneratorEnabled() const {
-  if (!GetBrowser())
+  if (!GetBrowser() || !GetProfile()) {
     return false;
+  }
+
+  if (sharing_hub::SharingIsDisabledByPolicy(GetProfile())) {
+    // If the sharing hub is disabled, clicking the QR code item (which tries to
+    // show the sharing hub) won't work.
+    return false;
+  }
 
   if (params_.media_type == ContextMenuDataMediaType::kImage) {
     return qrcode_generator::QRCodeGeneratorBubbleController::
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index 95c06f1..f713e059 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -1060,7 +1060,7 @@
                          testing::Bool());
 
 // Verify that Autofill context menu items are displayed on a plain text field.
-TEST_P(RenderViewContestMenuAutofillTest, ShowAutofillOptions) {
+TEST_P(RenderViewContestMenuAutofillTest, DISABLED_ShowAutofillOptions) {
   autofill::PersonalDataManager* pdm =
       autofill::PersonalDataManagerFactory::GetForProfile(
           profile()->GetOriginalProfile());
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 0cc6cc5..e83205b 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -313,6 +313,7 @@
   // session restore is handled by LifecycleManager.
   web_contents()->GetController().SetNeedsReload();
   web_contents()->GetController().LoadIfNecessary();
+  web_contents()->Focus();
   return true;
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
index 00aa4e9..7d980d7 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
@@ -24,18 +24,18 @@
   ]
 }
 
-tsc_output_dir = "$target_gen_dir/tsc"
-select_to_speak_tsc_output_folder = "$tsc_output_dir/select_to_speak"
+tsc_out_dir = "$target_gen_dir/tsc"
+select_to_speak_tsc_out_dir = "$tsc_out_dir/select_to_speak"
 
 # Add typescript files to compile here.
-ts_modules = []
+ts_modules = [ "select_to_speak/select_to_speak_main.ts" ]
 
 # Root dir must be the parent directory so it can reach common.
 ts_library("ts_build") {
   allow_js = true
   root_dir = "../"
   in_files = ts_modules
-  out_dir = tsc_output_dir
+  out_dir = tsc_out_dir
 
   definitions = []
 }
@@ -47,6 +47,7 @@
   dest_dir = select_to_speak_out_dir
   deps = [ ":ts_build" ]
   sources = [
+    "$select_to_speak_tsc_out_dir/select_to_speak_main.js",
     "background.html",
     "checked.png",
     "earcons/null_selection.ogg",
@@ -58,7 +59,6 @@
     "select_to_speak-2x.svg",
     "select_to_speak.js",
     "select_to_speak_constants.js",
-    "select_to_speak_main.js",
     "select_to_speak_options.js",
     "sts-icon-128.png",
     "sts-icon-16.png",
@@ -68,7 +68,7 @@
     "unchecked.png",
   ]
   rewrite_rules = [
-    rebase_path(select_to_speak_tsc_output_folder, root_build_dir) + ":",
+    rebase_path(select_to_speak_tsc_out_dir, root_build_dir) + ":",
     rebase_path(".", root_build_dir) + ":",
     rebase_path(closure_library_dir, root_build_dir) + ":closure",
   ]
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.ts
similarity index 88%
rename from chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
rename to chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.ts
index 7b44a80..781b1c1e 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.ts
@@ -6,7 +6,7 @@
 
 import {SelectToSpeak} from './select_to_speak.js';
 
-export let selectToSpeak;
+export let selectToSpeak: SelectToSpeak;
 
 if (InstanceChecker.isActiveInstance()) {
   selectToSpeak = new SelectToSpeak();
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.ts b/chrome/browser/resources/new_tab_page/lazy_load.ts
index 91644b2..072eab2 100644
--- a/chrome/browser/resources/new_tab_page/lazy_load.ts
+++ b/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -38,7 +38,7 @@
 export {FeedProxy} from './modules/feed/feed_module_proxy.js';
 export {feedDescriptor, FeedModuleElement, feedV2Descriptor} from './modules/feed/module.js';
 export {HistoryClustersProxy, HistoryClustersProxyImpl} from './modules/history_clusters/history_clusters_proxy.js';
-export {historyClustersDescriptor, HistoryClustersModuleElement} from './modules/history_clusters/module.js';
+export {HistoryClusterLayoutType, historyClustersDescriptor, HistoryClustersModuleElement} from './modules/history_clusters/module.js';
 export {InfoDialogElement} from './modules/info_dialog.js';
 export {InitializeModuleCallback, Module, ModuleDescriptor, ModuleDescriptorV2, ModuleHeight} from './modules/module_descriptor.js';
 export {counterfactualLoad} from './modules/module_descriptors.js';
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_1.html b/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_1.html
deleted file mode 100644
index fd95f3f..0000000
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_1.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<style>
-</style>
-<div>
-</div>
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_2.html b/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_2.html
deleted file mode 100644
index fd95f3f..0000000
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_2.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<style>
-</style>
-<div>
-</div>
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_3.html b/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_3.html
deleted file mode 100644
index fd95f3f..0000000
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/layout_3.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<style>
-</style>
-<div>
-</div>
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
index 0447b3e..33e453f 100644
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
+++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
@@ -21,4 +21,13 @@
   <ntp-history-clusters-tile id="main-tile" large-format
                              visit="[[cluster.visits.0]]">
   </ntp-history-clusters-tile>
+  <template is="dom-if" if="[[isLayout_('layout_1', layoutType)]]" restamp>
+    <div id="layout1" class="layout">Layout 1</div>
+  </template>
+  <template is="dom-if" if="[[isLayout_('layout_2', layoutType)]]" restamp>
+    <div id="layout2" class="layout">Layout 2</div>
+  </template>
+  <template is="dom-if" if="[[isLayout_('layout_3', layoutType)]]" restamp>
+    <div id="layout3" class="layout">Layout 3</div>
+  </template>
 </div>
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
index 297335a..5c80de02 100644
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
@@ -14,6 +14,17 @@
 import {HistoryClustersProxyImpl} from './history_clusters_proxy.js';
 import {getTemplate} from './module.html.js';
 
+export const LAYOUT_1_3_MIN_IMAGE_VISITS = 2;
+export const LAYOUT_2_IMAGE_VISITS = 1;
+export const LAYOUT_2_MIN_VISITS = 3;
+export const LAYOUT_3_MIN_VISITS = 4;
+
+export enum HistoryClusterLayoutType {
+  LAYOUT_1 = 'layout_1',  // 2 image visits
+  LAYOUT_2 = 'layout_2',  // 1 image visit & 2 non-image visits
+  LAYOUT_3 = 'layout_3',  // 2 image visits & 2 non-image visits
+}
+
 // TODO:(crbug.com/1410808): Add module UI logic.
 export class HistoryClustersModuleElement extends I18nMixin
 (PolymerElement) {
@@ -27,18 +38,24 @@
 
   static get properties() {
     return {
+      layoutType: String,
       /** The cluster displayed by this element. */
       cluster: Object,
     };
   }
 
   cluster: Cluster;
+  layoutType: HistoryClusterLayoutType;
+
+  private isLayout_(type: HistoryClusterLayoutType): boolean {
+    return type === this.layoutType;
+  }
 }
 
 customElements.define(
     HistoryClustersModuleElement.is, HistoryClustersModuleElement);
 
-async function createElement(): Promise<HTMLElement|null> {
+async function createElement(): Promise<HistoryClustersModuleElement|null> {
   const data =
       await HistoryClustersProxyImpl.getInstance().handler.getCluster();
   if (!data.cluster) {
@@ -47,8 +64,39 @@
 
   const element = new HistoryClustersModuleElement();
   element.cluster = data.cluster!;
+
+  const visits = element.cluster.visits;
+  // Count number of visits with images.
+  const imageCount = visits
+                         .filter((visit) => {
+                           return !!visit.imageUrl;
+                         })
+                         .length;
+  // Subtract the SRP from the visit count.
+  // The SRP is a visit that is included to be used in the module header
+  // and for opening the cluster in tab group.
+  const visitCount = visits.length - 1;
+
+  // Calculate which layout to use.
+  if (imageCount >= LAYOUT_1_3_MIN_IMAGE_VISITS) {
+    // Layout 1 and 3 require the same number of images.
+    // Decide which one to use by checking if there are enough total
+    // visits for layout 3.
+    if (visitCount >= LAYOUT_3_MIN_VISITS) {
+      element.layoutType = HistoryClusterLayoutType.LAYOUT_3;
+    } else {
+      element.layoutType = HistoryClusterLayoutType.LAYOUT_1;
+    }
+  } else if (imageCount === LAYOUT_2_IMAGE_VISITS &&
+      visitCount >= LAYOUT_2_MIN_VISITS) {
+    element.layoutType = HistoryClusterLayoutType.LAYOUT_2;
+  } else {
+    // If the data doesn't fit any layout, don't show the module.
+    return null;
+  }
+
   return element;
 }
 
 export const historyClustersDescriptor: ModuleDescriptor = new ModuleDescriptor(
-    /*id=*/ 'history-clusters', createElement);
+    /*id=*/ 'history_clusters', createElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
index 5329dd3..d7880f1 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
@@ -107,8 +107,3 @@
     </os-settings-subpage>
   </template>
 </os-settings-animated-pages>
-<template is="dom-if" if="[[showSignoutDialog_]]" restamp>
-  <os-settings-signout-dialog sync-status="[[syncStatus]]"
-      on-close="onDisconnectDialogClosed_">
-  </os-settings-signout-dialog>
-</template>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.ts b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.ts
index 7288bbd..4051cb03 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.ts
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.ts
@@ -22,11 +22,9 @@
 import './lock_screen.js';
 import './lock_screen_password_prompt_dialog.js';
 import './os_sync_controls.js';
-import './os_signout_dialog.js';
 import './os_sync_page.js';
 
 import {convertImageSequenceToPng} from 'chrome://resources/ash/common/cr_picture/png.js';
-import {focusWithoutInk} from 'chrome://resources/ash/common/focus_without_ink_js.js';
 import {sendWithPromise} from 'chrome://resources/js/cr.js';
 import {getImage} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -35,7 +33,6 @@
 import {Setting} from '../../mojom-webui/setting.mojom-webui.js';
 import {ProfileInfo, ProfileInfoBrowserProxyImpl} from '../../people_page/profile_info_browser_proxy.js';
 import {SyncBrowserProxy, SyncBrowserProxyImpl, SyncStatus} from '../../people_page/sync_browser_proxy.js';
-import {castExists} from '../assert_extras.js';
 import {DeepLinkingMixin} from '../deep_linking_mixin.js';
 import {LockStateMixin} from '../lock_state_mixin.js';
 import {OsPageVisibility} from '../os_page_visibility.js';
@@ -90,8 +87,6 @@
 
       profileLabel_: String,
 
-      showSignoutDialog_: Boolean,
-
       fingerprintUnlockEnabled_: {
         type: Boolean,
         value() {
@@ -178,7 +173,6 @@
   private profileName_: string;
   private profileEmail_: string;
   private profileLabel_: string;
-  private showSignoutDialog_: boolean;
   private fingerprintUnlockEnabled_: boolean;
   private isAccountManagerEnabled_: boolean;
   private showParentalControls_: boolean;
@@ -314,17 +308,6 @@
   }
 
   override currentRouteChanged(route: Route): void {
-    if (Router.getInstance().currentRoute === routes.OS_SIGN_OUT) {
-      // If the sync status has not been fetched yet, optimistically display
-      // the sign-out dialog. There is another check when the sync status is
-      // fetched. The dialog will be closed when the user is not signed in.
-      if (this.syncStatus && !this.syncStatus.signedIn) {
-        Router.getInstance().navigateToPreviousRoute();
-      } else {
-        this.showSignoutDialog_ = true;
-      }
-    }
-
     // The old sync page is a shared subpage, so we handle deep links for
     // both this page and the sync page. Not ideal.
     if (route === routes.SYNC || route === routes.OS_PEOPLE) {
@@ -399,22 +382,6 @@
     }
   }
 
-  private onDisconnectDialogClosed_(): void {
-    this.showSignoutDialog_ = false;
-    focusWithoutInk(
-        castExists(this.shadowRoot!.querySelector<HTMLButtonElement>(
-            '#disconnectButton')),
-    );
-
-    if (Router.getInstance().currentRoute === routes.OS_SIGN_OUT) {
-      Router.getInstance().navigateToPreviousRoute();
-    }
-  }
-
-  private onDisconnectTap_(): void {
-    Router.getInstance().navigateTo(routes.OS_SIGN_OUT);
-  }
-
   private onSyncTap_(): void {
     // Users can go to sync subpage regardless of sync status.
     Router.getInstance().navigateTo(routes.SYNC);
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_signout_dialog.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_signout_dialog.html
deleted file mode 100644
index 7b5c7bf..0000000
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_signout_dialog.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<style include="cr-shared-style settings-shared iron-flex">
-  .delete-profile-warning {
-    padding-bottom: 10px;
-    padding-inline-end: var(--cr-section-padding);
-    /* In order to line up with the checkbox text. */
-    padding-inline-start: calc(var(--cr-section-padding) + 32px);
-    padding-top: 10px;
-  }
-
-  #wideFooter {
-    /* Override the cr-dialog footer padding. */
-    padding: 0;
-  }
-
-  #dialog-body {
-    /* Add space for the link focus ring. See https://crbug.com/916939. */
-    padding-bottom: 2px;
-  }
-</style>
-
-<cr-dialog id="dialog" ignore-enter-key close-text="$i18n{close}">
-  <div slot="title">$i18n{syncDisconnectTitle}</div>
-  <div id="dialog-body" slot="body">
-    <div inner-h-t-m-l="[[
-        getDisconnectExplanationHtml_(syncStatus.domain)]]">
-    </div>
-  </div>
-  <div slot="button-container">
-    <cr-button id="disconnectCancel" class="cancel-button"
-        on-click="onDisconnectCancel_">
-      $i18n{cancel}
-    </cr-button>
-    <cr-button id="disconnectConfirm" class="action-button"
-        hidden="[[syncStatus.domain]]" on-click="onDisconnectConfirm_">
-      $i18n{syncDisconnect}
-    </cr-button>
-    <cr-button id="disconnectManagedProfileConfirm"
-        class="action-button" hidden="[[!syncStatus.domain]]"
-        on-click="onDisconnectConfirm_">
-      $i18n{syncDisconnectConfirm}
-    </cr-button>
-  </div>
-</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_signout_dialog.ts b/chrome/browser/resources/settings/chromeos/os_people_page/os_signout_dialog.ts
deleted file mode 100644
index de454d85..0000000
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_signout_dialog.ts
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview 'settings-signout-dialog' is a dialog that allows the
- * user to turn off sync and sign out of Chromium.
- */
-import '//resources/cr_elements/cr_button/cr_button.js';
-import '//resources/cr_elements/cr_dialog/cr_dialog.js';
-import '//resources/cr_elements/cr_shared_style.css.js';
-import '//resources/cr_elements/cr_shared_vars.css.js';
-import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
-import '../../settings_shared.css.js';
-
-import {CrDialogElement} from '//resources/cr_elements/cr_dialog/cr_dialog.js';
-import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js';
-import {sanitizeInnerHtml} from '//resources/js/parse_html_subset.js';
-import {microTask, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-
-import {SyncBrowserProxyImpl, SyncStatus} from '../../people_page/sync_browser_proxy.js';
-
-import {getTemplate} from './os_signout_dialog.html.js';
-
-export interface OsSettingsSignoutDialogElement {
-  $: {
-    dialog: CrDialogElement,
-    disconnectConfirm: HTMLElement,
-  };
-}
-
-const OsSettingsSignoutDialogElementBase = WebUiListenerMixin(PolymerElement);
-
-export class OsSettingsSignoutDialogElement extends
-    OsSettingsSignoutDialogElementBase {
-  static get is() {
-    return 'os-settings-signout-dialog';
-  }
-
-  static get template() {
-    return getTemplate();
-  }
-
-  static get properties() {
-    return {
-      /**
-       * The current sync status, supplied by the parent.
-       */
-      syncStatus: {
-        type: Object,
-        observer: 'syncStatusChanged_',
-      },
-
-      /**
-       * True if the checkbox to delete the profile has been checked.
-       */
-      deleteProfile_: Boolean,
-
-      /**
-       * True if the profile deletion warning is visible.
-       */
-      deleteProfileWarningVisible_: Boolean,
-
-      /**
-       * The profile deletion warning. The message indicates the number of
-       * profile stats that will be deleted if a non-zero count for the profile
-       * stats is returned from the browser.
-       */
-      deleteProfileWarning_: String,
-    };
-  }
-
-  syncStatus: SyncStatus|null;
-  private deleteProfile_: boolean;
-  private deleteProfileWarningVisible_: boolean;
-  private deleteProfileWarning_: string;
-
-  override connectedCallback() {
-    super.connectedCallback();
-
-    this.addWebUiListener(
-        'profile-stats-count-ready', this.handleProfileStatsCount_.bind(this));
-    microTask.run(() => {
-      this.$.dialog.showModal();
-    });
-  }
-
-  /**
-   * @return true when the user selected 'Confirm'.
-   */
-  wasConfirmed(): boolean {
-    return this.$.dialog.getNative().returnValue === 'success';
-  }
-
-  /**
-   * Handler for when the profile stats count is pushed from the browser.
-   */
-  private handleProfileStatsCount_(count: number) {
-    const username = this.syncStatus!.signedInUsername || '';
-    if (count === 0) {
-      this.deleteProfileWarning_ = loadTimeData.getStringF(
-          'deleteProfileWarningWithoutCounts', username);
-    } else if (count === 1) {
-      this.deleteProfileWarning_ = loadTimeData.getStringF(
-          'deleteProfileWarningWithCountsSingular', username);
-    } else {
-      this.deleteProfileWarning_ = loadTimeData.getStringF(
-          'deleteProfileWarningWithCountsPlural', count, username);
-    }
-  }
-
-  /**
-   * Polymer observer for syncStatus.
-   */
-  private syncStatusChanged_() {
-    if (!this.syncStatus!.signedIn && this.$.dialog.open) {
-      this.$.dialog.close();
-    }
-  }
-
-  private getDisconnectExplanationHtml_(_domain: string): TrustedHTML {
-    return sanitizeInnerHtml(
-        loadTimeData.getString('syncDisconnectExplanation'));
-  }
-
-  private onDisconnectCancel_() {
-    this.$.dialog.cancel();
-  }
-
-  private onDisconnectConfirm_() {
-    this.$.dialog.close();
-    // Chrome OS users are always signed-in, so just turn off sync.
-    SyncBrowserProxyImpl.getInstance().turnOffSync();
-  }
-
-  private isDeleteProfileFooterVisible_(): boolean {
-    return !this.syncStatus!.domain;
-  }
-}
-
-declare global {
-  interface HTMLElementTagNameMap {
-    'os-settings-signout-dialog': OsSettingsSignoutDialogElement;
-  }
-}
-
-customElements.define(
-    OsSettingsSignoutDialogElement.is, OsSettingsSignoutDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index be5d341..310d898 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -193,7 +193,6 @@
   "chromeos/os_people_page/lock_screen_password_prompt_dialog.ts",
   "chromeos/os_people_page/os_people_page.ts",
   "chromeos/os_people_page/os_personalization_options.ts",
-  "chromeos/os_people_page/os_signout_dialog.ts",
   "chromeos/os_people_page/os_sync_controls.ts",
   "chromeos/os_people_page/os_sync_encryption_options.ts",
   "chromeos/os_people_page/os_sync_page.ts",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_routes.ts b/chrome/browser/resources/settings/chromeos/os_settings_routes.ts
index b89a7b33..8c0e87d 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_routes.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings_routes.ts
@@ -178,7 +178,6 @@
   OS_PRIVACY: Route;
   OS_RESET: Route;
   OS_SEARCH: Route;
-  OS_SIGN_OUT: Route;
   OS_SYNC: Route;
   OS_PEOPLE: Route;
   PER_DEVICE_KEYBOARD: Route;
@@ -220,13 +219,12 @@
   const r: Partial<OsSettingsRoutes> = {};
   const {Section, Subpage} = routesMojomWebui;
 
-  // Special routes: BASIC is the main page which loads if no path is
-  // provided, ADVANCED is the bottom section of the main page which is not
-  // visible unless the user enables it, and OS_SIGN_OUT is a sign out dialog.
+  // Special routes:
+  // - BASIC is the main page which loads if no path is provided
+  // - ADVANCED is the bottom section of the main page which is not
+  //   visible unless the user enables it
   r.BASIC = new Route('/');
   r.ADVANCED = new Route('/advanced');
-  r.OS_SIGN_OUT = r.BASIC.createChild('/osSignOut');
-  r.OS_SIGN_OUT.isNavigableDialog = true;
 
   // Network section.
   r.INTERNET = createSection(
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
index 04ba9123..1be2271 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
@@ -13,7 +13,7 @@
 
   cr-icon-button {
     --cr-icon-button-icon-size: 16px;
-    --cr-icon-button-size: 20px;
+    --cr-icon-button-size: 24px;
     margin: 0;
   }
 
@@ -82,6 +82,10 @@
     margin: 8px 14px;
   }
 
+  :host-context([chrome-refresh-2023]) sp-heading {
+    margin: 8px 16px;
+  }
+
   .icon-button-row {
     align-items: center;
     color: var(--cr-secondary-text-color);
@@ -149,12 +153,15 @@
             activeFolderPath_.*, editing_)]]">
       <h1 slot="heading">[[getActiveFolderLabel_(activeFolderPath_.*)]]</h1>
 
-      <cr-button slot="buttons" class="sort-menu-button"
-          aria-label="$i18n{sortMenuA11yLabel}"
-          on-click="onShowSortMenuClicked_">
+      <div aria-hidden="true" slot="metadata">
         [[getSortLabel_(activeSortIndex_)]]
-        <iron-icon icon="sp:filter-list"></iron-icon>
-      </cr-button>
+      </div>
+      <cr-icon-button slot="buttons" class="sort-menu-button"
+          iron-icon="sp:filter-list"
+          aria-label="$i18n{sortMenuA11yLabel}"
+          aria-description="[[getSortLabel_(activeSortIndex_)]]"
+          on-click="onShowSortMenuClicked_">
+      </cr-icon-button>
       <cr-icon-button slot="buttons" iron-icon="bookmarks:create-new-folder"
           disabled="[[editing_]]"
           aria-label="$i18n{createNewFolderA11yLabel}"
diff --git a/chrome/browser/resources/side_panel/shared/sp_heading.html b/chrome/browser/resources/side_panel/shared/sp_heading.html
index dcd237fe..b681044 100644
--- a/chrome/browser/resources/side_panel/shared/sp_heading.html
+++ b/chrome/browser/resources/side_panel/shared/sp_heading.html
@@ -16,10 +16,12 @@
 
   #backButton,
   ::slotted(cr-icon-button[slot=buttons]) {
-    --cr-icon-button-margin-start: -2px;
-    --cr-icon-button-margin-end: -2px;
     --cr-icon-button-icon-size: 16px;
-    --cr-icon-button-size: 20px;
+    --cr-icon-button-size: 24px;
+  }
+
+  #backButton {
+    margin: 0 -4px;
   }
 
   :host-context([chrome-refresh-2023]) #backButton {
@@ -70,35 +72,22 @@
     font-weight: 400;
     line-height: 20px;
     overflow: hidden;
+    padding-inline-end: 4px;
     text-overflow: ellipsis;
     white-space: nowrap;
   }
 
   :host-context([chrome-refresh-2023]) ::slotted([slot=metadata]) {
     line-height: 16px;
-    padding-inline-end: 4px;
   }
 
   #suffixButtons {
     align-items: center;
-    display: flex;
+    display: grid;
     gap: 8px;
-  }
-
-  ::slotted(cr-button[slot=buttons]) {
-    --hover-bg-color: var(--cr-hover-background-color);
-    --ripple-opacity: 0;
-    border: none;
-    color: var(--cr-secondary-text-color);
-    font-weight: 400;
-    gap: 4px;
-    height: 20px;
-    padding: 0;
-  }
-
-  ::slotted(cr-button[slot=buttons]:active) {
-    background-color: var(--cr-active-background-color);
-    box-shadow: none;
+    grid-auto-columns: 16px;
+    grid-auto-flow: column;
+    justify-items: center;
   }
 </style>
 
diff --git a/chrome/browser/resources/side_panel/user_notes/user_notes_api_proxy.ts b/chrome/browser/resources/side_panel/user_notes/user_notes_api_proxy.ts
index da91ab83..51d7e8a 100644
--- a/chrome/browser/resources/side_panel/user_notes/user_notes_api_proxy.ts
+++ b/chrome/browser/resources/side_panel/user_notes/user_notes_api_proxy.ts
@@ -66,6 +66,10 @@
     this.handler.setSortOrder(sortByNewest);
   }
 
+  hasNotesOnAnyPages() {
+    return this.handler.hasNotesInAnyPages();
+  }
+
   getCallbackRouter() {
     return this.callbackRouter;
   }
diff --git a/chrome/browser/resources/tab_strip/tab.ts b/chrome/browser/resources/tab_strip/tab.ts
index cb713b94..4f77247c 100644
--- a/chrome/browser/resources/tab_strip/tab.ts
+++ b/chrome/browser/resources/tab_strip/tab.ts
@@ -260,49 +260,6 @@
     this.toggleAttribute('touch_pressed_', isTouchPressed);
   }
 
-  slideIn(): Promise<void> {
-    const paddingInlineEnd = getPaddingInlineEndProperty();
-
-    // If this TabElement is the last tab, there needs to be enough space for
-    // the view to scroll to it. Therefore, immediately take up all the space
-    // it needs to and only animate the scale.
-    const isLastChild = this.nextElementSibling === null;
-
-    const startState = {
-      maxWidth: isLastChild ? 'var(--tabstrip-tab-width)' : 0,
-      transform: `scale(0)`,
-      [paddingInlineEnd]: isLastChild ? 'var(--tabstrip-tab-spacing)' : 0,
-    };
-
-    const finishState = {
-      maxWidth: `var(--tabstrip-tab-width)`,
-      transform: `scale(1)`,
-      [paddingInlineEnd]: 'var(--tabstrip-tab-spacing)',
-    };
-
-    return new Promise(resolve => {
-      const animation = this.animate([startState, finishState], {
-        duration: 300,
-        easing: 'cubic-bezier(.4, 0, 0, 1)',
-      });
-      animation.onfinish = () => {
-        resolve();
-      };
-
-      // TODO(crbug.com/1035678) By the next animation frame, the animation
-      // should start playing. By the time another animation frame happens,
-      // force play the animation if the animation has not yet begun. Remove
-      // if/when the Blink issue has been fixed.
-      requestAnimationFrame(() => {
-        requestAnimationFrame(() => {
-          if (animation.pending) {
-            animation.play();
-          }
-        });
-      });
-    });
-  }
-
   slideOut(): Promise<void> {
     if (!this.tabsApi_.isVisible() || this.tab_.pinned ||
         this.tabSwiper_.wasSwiping()) {
diff --git a/chrome/browser/resources/tab_strip/tab_list.ts b/chrome/browser/resources/tab_strip/tab_list.ts
index be1db0f..6e903bd 100644
--- a/chrome/browser/resources/tab_strip/tab_list.ts
+++ b/chrome/browser/resources/tab_strip/tab_list.ts
@@ -556,7 +556,6 @@
 
     const tabElement = this.createTabElement_(tab);
     this.placeTabElement(tabElement, tab.index, tab.pinned, tab.groupId);
-    this.addAnimationPromise_(tabElement.slideIn());
     if (tab.active) {
       this.updatePreviouslyActiveTabs_(tab.id);
       this.scrollToTab_(tabElement);
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
index 3ac1299..cc0bdc7 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
@@ -85,6 +85,7 @@
 
 constexpr char kFileDataProcessTimestampPref[] = "last_processed_timestamp";
 constexpr char kFileDataDictPref[] = "file_data";
+constexpr char kManifestFile[] = "manifest.json";
 
 // Delay before the Telemetry Service checks its last upload time.
 base::TimeDelta kStartupUploadCheckDelaySeconds = base::Seconds(15);
@@ -95,6 +96,17 @@
       persisted_at_write_interval);
 }
 
+void RecordNumOffstoreExtensions(int num_extensions) {
+  base::UmaHistogramCounts100(
+      "SafeBrowsing.ExtensionTelemetry.FileData.NumOffstoreExtensions",
+      num_extensions);
+}
+
+void RecordCollectionDuration(base::TimeDelta duration) {
+  base::UmaHistogramMediumTimes(
+      "SafeBrowsing.ExtensionTelemetry.FileData.CollectionDuration", duration);
+}
+
 static_assert(extensions::Manifest::NUM_LOAD_TYPES == 10,
               "ExtensionTelemetryReportRequest::ExtensionInfo::Type "
               "needs to match extensions::Manifest::Type.");
@@ -226,8 +238,9 @@
 
 void ExtensionTelemetryService::SetEnabled(bool enable) {
   // Make call idempotent.
-  if (enabled_ == enable)
+  if (enabled_ == enable) {
     return;
+  }
 
   enabled_ = enable;
   if (enabled_) {
@@ -417,8 +430,9 @@
 void ExtensionTelemetryService::CreateAndUploadReport() {
   DCHECK(enabled_);
   active_report_ = CreateReport();
-  if (!active_report_)
+  if (!active_report_) {
     return;
+  }
 
   auto upload_data = std::make_unique<std::string>();
   if (!active_report_->SerializeToString(upload_data.get())) {
@@ -490,8 +504,9 @@
   // This check is performed as a delayed task after enabling the service. The
   // service may become disabled between the time this task is scheduled and it
   // actually runs. So make sure service is enabled before performing the check.
-  if (!enabled_)
+  if (!enabled_) {
     return;
+  }
 
   if ((GetLastUploadTimeForExtensionTelemetry(*pref_service_) +
        current_reporting_interval_) <= base::Time::Now()) {
@@ -510,8 +525,9 @@
   } else {
     // Otherwise persist data gathered so far.
     active_report_ = CreateReport();
-    if (!active_report_)
+    if (!active_report_) {
       return;
+    }
     std::string write_string;
     if (!active_report_->SerializeToString(&write_string)) {
       active_report_.reset();
@@ -529,8 +545,9 @@
   // extension store is empty) AND there are no installed extensions currently.
   std::unique_ptr<extensions::ExtensionSet> installed_extensions =
       extension_registry_->GenerateInstalledExtensionsSet();
-  if (extension_store_.empty() && installed_extensions->is_empty())
+  if (extension_store_.empty() && installed_extensions->is_empty()) {
     return nullptr;
+  }
 
   auto telemetry_report_pb =
       std::make_unique<ExtensionTelemetryReportRequest>();
@@ -666,6 +683,46 @@
   DVLOG(1) << "Telemetry Report: " << ss.str();
 }
 
+ExtensionTelemetryService::OffstoreExtensionFileData::
+    OffstoreExtensionFileData() = default;
+ExtensionTelemetryService::OffstoreExtensionFileData::
+    ~OffstoreExtensionFileData() = default;
+ExtensionTelemetryService::OffstoreExtensionFileData::
+    OffstoreExtensionFileData::OffstoreExtensionFileData(
+        const OffstoreExtensionFileData& src) = default;
+
+absl::optional<ExtensionTelemetryService::OffstoreExtensionFileData>
+ExtensionTelemetryService::RetrieveOffstoreFileDataForReport(
+    const extensions::ExtensionId& extension_id) {
+  const auto& pref_dict = GetExtensionTelemetryFileData(*pref_service_);
+  const base::Value::Dict* extension_dict = pref_dict.FindDict(extension_id);
+  if (!extension_dict) {
+    return absl::nullopt;
+  }
+
+  const base::Value::Dict* file_data_dict =
+      extension_dict->FindDict(kFileDataDictPref);
+  if (!file_data_dict || file_data_dict->empty()) {
+    return absl::nullopt;
+  }
+
+  OffstoreExtensionFileData offstore_extension_file_data;
+  base::Value::Dict dict = file_data_dict->Clone();
+  absl::optional<base::Value> manifest_value = dict.Extract(kManifestFile);
+  if (manifest_value.has_value()) {
+    offstore_extension_file_data.manifest =
+        std::move(manifest_value.value().GetString());
+  }
+
+  for (auto&& [file_name, file_hash] : dict) {
+    ExtensionTelemetryReportRequest_ExtensionInfo_FileInfo file_info;
+    file_info.set_name(std::move(file_name));
+    file_info.set_hash(std::move(file_hash.GetString()));
+    offstore_extension_file_data.file_infos.emplace_back(std::move(file_info));
+  }
+  return absl::make_optional(offstore_extension_file_data);
+}
+
 std::unique_ptr<ExtensionInfo>
 ExtensionTelemetryService::GetExtensionInfoForReport(
     const extensions::Extension& extension) {
@@ -692,6 +749,19 @@
   extension_info->set_disable_reasons(
       extension_prefs_->GetDisableReasons(extension.id()));
 
+  if (base::FeatureList::IsEnabled(kExtensionTelemetryFileData)) {
+    absl::optional<OffstoreExtensionFileData> offstore_file_data =
+        RetrieveOffstoreFileDataForReport(extension.id());
+
+    if (offstore_file_data.has_value()) {
+      extension_info->set_manifest_json(
+          std::move(offstore_file_data.value().manifest));
+      for (auto& file_info : offstore_file_data.value().file_infos) {
+        extension_info->mutable_file_infos()->Add(std::move(file_info));
+      }
+    }
+  }
+
   return extension_info;
 }
 
@@ -722,6 +792,7 @@
     return;
   }
 
+  offstore_file_data_collection_start_time_ = base::TimeTicks::Now();
   offstore_extension_dirs_.clear();
   offstore_extension_file_data_contexts_.clear();
   GetOffstoreExtensionDirs();
@@ -765,6 +836,7 @@
       offstore_extension_dirs_[extension->id()] = extension->path();
     }
   }
+  RecordNumOffstoreExtensions(offstore_extension_dirs_.size());
 }
 
 void ExtensionTelemetryService::RemoveUninstalledExtensionsFileDataFromPref() {
@@ -773,10 +845,10 @@
   base::Value::Dict& pref_dict = pref_update.Get();
 
   std::vector<extensions::ExtensionId> uninstalled_extensions;
-  for (auto offstore : pref_dict) {
+  for (auto&& offstore : pref_dict) {
     if (offstore_extension_dirs_.find(offstore.first) ==
         offstore_extension_dirs_.end()) {
-      uninstalled_extensions.push_back(offstore.first);
+      uninstalled_extensions.emplace_back(offstore.first);
     }
   }
 
@@ -798,6 +870,12 @@
         base::Seconds(
             kExtensionTelemetryFileDataCollectionIntervalSeconds.Get()),
         this, &ExtensionTelemetryService::StartOffstoreFileDataCollection);
+
+    // Record only if there are off-store extensions installed.
+    if (!offstore_extension_dirs_.empty()) {
+      RecordCollectionDuration(base::TimeTicks::Now() -
+                               offstore_file_data_collection_start_time_);
+    }
     return;
   }
 
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
index 79beac2..73c8ce97 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
@@ -44,6 +44,7 @@
 class ExtensionTelemetryPersister;
 class ExtensionTelemetryReportRequest;
 class ExtensionTelemetryReportRequest_ExtensionInfo;
+class ExtensionTelemetryReportRequest_ExtensionInfo_FileInfo;
 class ExtensionTelemetryUploader;
 class SafeBrowsingTokenFetcher;
 
@@ -190,6 +191,22 @@
   // Stops and clears any offstore file data collection objects/contexts.
   void StopOffstoreFileDataCollection();
 
+  // Stores offstore extension file data retrieved from PrefService.
+  struct OffstoreExtensionFileData {
+    OffstoreExtensionFileData();
+    ~OffstoreExtensionFileData();
+    OffstoreExtensionFileData(const OffstoreExtensionFileData&);
+
+    std::string manifest;
+    std::vector<ExtensionTelemetryReportRequest_ExtensionInfo_FileInfo>
+        file_infos;
+  };
+
+  // Given an |extension_id|, retrieves the collected file data from PrefService
+  // if available.
+  absl::optional<OffstoreExtensionFileData> RetrieveOffstoreFileDataForReport(
+      const extensions::ExtensionId& extension_id);
+
   // The persister object is bound to the threadpool. This prevents the
   // the read/write operations the `persister_` runs from blocking
   // the UI thread. It also allows the `persister_` object to be
@@ -254,6 +271,7 @@
   // Then repeat the collection based on
   // |kExtensionTelemetryFileDataProcessIntervalSeconds| - default: 2 hours.
   base::OneShotTimer offstore_file_data_collection_timer_;
+  base::TimeTicks offstore_file_data_collection_start_time_;
 
   using SignalProcessors =
       base::flat_map<ExtensionSignalType,
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
index 03d38025..8e1644a 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
@@ -103,8 +103,9 @@
   const ExtensionInfo* GetExtensionInfoFromExtensionStore(
       const ExtensionId& extension_id) {
     auto iter = telemetry_service_->extension_store_.find(extension_id);
-    if (iter == telemetry_service_->extension_store_.end())
+    if (iter == telemetry_service_->extension_store_.end()) {
       return nullptr;
+    }
     return iter->second.get();
   }
 
@@ -864,4 +865,114 @@
   EXPECT_TRUE(actual_extension_1->FindDict(kFileDataDictPref));
 }
 
+TEST_F(ExtensionTelemetryServiceTest,
+       FileData_AttachesOffstoreFileDataToReport) {
+  // Enable |kExtensionTelemetryFileData| feature and starts collection.
+  telemetry_service_->SetEnabled(false);
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      {kExtensionTelemetryFileData},
+      {{"StartupDelaySeconds",
+        base::NumberToString(kFileDataStartUpDelaySeconds)}});
+  telemetry_service_->SetEnabled(true);
+  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.RunUntilIdle();
+
+  std::unique_ptr<TelemetryReport> telemetry_report_pb = GetTelemetryReport();
+  const auto& file_data_dict =
+      profile_.GetPrefs()->GetDict(prefs::kExtensionTelemetryFileData);
+
+  const base::Value::Dict* extension_0_dict =
+      file_data_dict.FindDict(kExtensionId[0])->FindDict(kFileDataDictPref);
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().id(), kExtensionId[0]);
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().manifest_json(),
+            *(extension_0_dict->FindString(kManifestFile)));
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().file_infos_size(), 1);
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().file_infos(0).name(),
+            kJavaScriptFile);
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().file_infos(0).hash(),
+            *(extension_0_dict->FindString(kJavaScriptFile)));
+
+  const base::Value::Dict* extension_1_dict =
+      file_data_dict.FindDict(kExtensionId[1])->FindDict(kFileDataDictPref);
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().id(), kExtensionId[1]);
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().manifest_json(),
+            *(extension_1_dict->FindString(kManifestFile)));
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().file_infos_size(), 1);
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().file_infos(0).name(),
+            kJavaScriptFile);
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().file_infos(0).hash(),
+            *(extension_1_dict->FindString(kJavaScriptFile)));
+}
+
+TEST_F(ExtensionTelemetryServiceTest,
+       FileData_DoesNotAttachFileDataForNonOffstoreExtensions) {
+  // Register webstore extension 2.
+  RegisterExtensionWithExtensionService(kExtensionId[2], kExtensionName[2],
+                                        ManifestLocation::kInternal,
+                                        Extension::FROM_WEBSTORE);
+  // Enable |kExtensionTelemetryFileData| feature and starts collection.
+  telemetry_service_->SetEnabled(false);
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      {kExtensionTelemetryFileData},
+      {{"StartupDelaySeconds",
+        base::NumberToString(kFileDataStartUpDelaySeconds)}});
+  telemetry_service_->SetEnabled(true);
+  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.RunUntilIdle();
+
+  std::unique_ptr<TelemetryReport> telemetry_report_pb = GetTelemetryReport();
+
+  // Verify that Extension 0 has offstore file data.
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().id(), kExtensionId[0]);
+  EXPECT_FALSE(
+      telemetry_report_pb->reports(0).extension().manifest_json().empty());
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().file_infos_size(), 1);
+
+  // Verify Extension 1 has offstore file data.
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().id(), kExtensionId[1]);
+  EXPECT_FALSE(
+      telemetry_report_pb->reports(1).extension().manifest_json().empty());
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().file_infos_size(), 1);
+
+  // Verify Extension 2 does not have offstore file data.
+  EXPECT_EQ(telemetry_report_pb->reports(2).extension().id(), kExtensionId[2]);
+  EXPECT_FALSE(telemetry_report_pb->reports(2).extension().has_manifest_json());
+  EXPECT_EQ(telemetry_report_pb->reports(2).extension().file_infos_size(), 0);
+}
+
+TEST_F(ExtensionTelemetryServiceTest, FileData_HandlesEmptyFileDataInPrefs) {
+  // Enable |kExtensionTelemetryFileData| feature and starts collection.
+  telemetry_service_->SetEnabled(false);
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      {kExtensionTelemetryFileData},
+      {{"StartupDelaySeconds",
+        base::NumberToString(kFileDataStartUpDelaySeconds)}});
+  telemetry_service_->SetEnabled(true);
+  task_environment_.FastForwardBy(base::Seconds(kFileDataStartUpDelaySeconds));
+  task_environment_.RunUntilIdle();
+
+  // Set up pref dict:
+  // extension 0 - empty file data dict
+  // extension 1 - missing file data dict key
+  base::Value::Dict extension_0_dict;
+  extension_0_dict.Set(kFileDataDictPref, base::Value::Dict());
+  base::Value::Dict empty_file_data_dicts;
+  empty_file_data_dicts.Set(kExtensionId[0], std::move(extension_0_dict));
+  empty_file_data_dicts.Set(kExtensionId[1], base::Value::Dict());
+  profile_.GetPrefs()->SetDict(prefs::kExtensionTelemetryFileData,
+                               std::move(empty_file_data_dicts));
+
+  std::unique_ptr<TelemetryReport> telemetry_report_pb = GetTelemetryReport();
+
+  // Verify Extension 0 does not have offstore file data.
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().id(), kExtensionId[0]);
+  EXPECT_FALSE(telemetry_report_pb->reports(0).extension().has_manifest_json());
+  EXPECT_EQ(telemetry_report_pb->reports(0).extension().file_infos_size(), 0);
+
+  // Verify Extension 1 does not have offstore file data.
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().id(), kExtensionId[1]);
+  EXPECT_FALSE(telemetry_report_pb->reports(1).extension().has_manifest_json());
+  EXPECT_EQ(telemetry_report_pb->reports(1).extension().file_infos_size(), 0);
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/search_engines/android/BUILD.gn b/chrome/browser/search_engines/android/BUILD.gn
index 0b573c6..59f2c7f 100644
--- a/chrome/browser/search_engines/android/BUILD.gn
+++ b/chrome/browser/search_engines/android/BUILD.gn
@@ -31,6 +31,7 @@
     "//chrome/browser/omaha/android:java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
+    "//chrome/browser/settings:java",
     "//chrome/browser/ui/messages/android:java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/site_settings/android:java",
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java
index 919020f..11aae72d 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java
+++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java
@@ -73,7 +73,8 @@
     }
 
     /** The current context. */
-    private Context mContext;
+    private final Context mContext;
+    private final Profile mProfile;
 
     /** The layout inflater to use for the custom views. */
     private LayoutInflater mLayoutInflater;
@@ -107,9 +108,11 @@
     /**
      * Construct a SearchEngineAdapter.
      * @param context The current context.
+     * @param profile The Profile associated with these settings.
      */
-    public SearchEngineAdapter(Context context) {
+    public SearchEngineAdapter(Context context, Profile profile) {
         mContext = context;
+        mProfile = profile;
         mLayoutInflater =
                 (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     }
@@ -119,7 +122,7 @@
      */
     public void start() {
         refreshData();
-        TemplateUrlServiceFactory.get().addObserver(this);
+        TemplateUrlServiceFactory.getForProfile(mProfile).addObserver(this);
     }
 
     /**
@@ -127,11 +130,11 @@
      */
     public void stop() {
         if (mHasLoadObserver) {
-            TemplateUrlServiceFactory.get().unregisterLoadListener(this);
+            TemplateUrlServiceFactory.getForProfile(mProfile).unregisterLoadListener(this);
             mHasLoadObserver = false;
         }
 
-        TemplateUrlServiceFactory.get().removeObserver(this);
+        TemplateUrlServiceFactory.getForProfile(mProfile).removeObserver(this);
     }
 
     @VisibleForTesting
@@ -153,7 +156,7 @@
      * Initialize the search engine list.
      */
     private void refreshData() {
-        TemplateUrlService templateUrlService = TemplateUrlServiceFactory.get();
+        TemplateUrlService templateUrlService = TemplateUrlServiceFactory.getForProfile(mProfile);
         if (!templateUrlService.isLoaded()) {
             mHasLoadObserver = true;
             templateUrlService.registerLoadListener(this);
@@ -201,11 +204,11 @@
         }
 
         if (mSelectedSearchEnginePosition == -1) {
-            throw new IllegalStateException(
-                    String.format("Default search engine is not found in available search engines:"
-                                    + " DSE is valid=%b, is managed=%b",
-                            defaultSearchEngineTemplateUrl != null,
-                            TemplateUrlServiceFactory.get().isDefaultSearchManaged()));
+            throw new IllegalStateException(String.format(
+                    "Default search engine is not found in available search engines:"
+                            + " DSE is valid=%b, is managed=%b",
+                    defaultSearchEngineTemplateUrl != null,
+                    TemplateUrlServiceFactory.getForProfile(mProfile).isDefaultSearchManaged()));
         }
 
         mInitialEnginePosition = mSelectedSearchEnginePosition;
@@ -409,7 +412,7 @@
 
     @Override
     public void onTemplateUrlServiceLoaded() {
-        TemplateUrlServiceFactory.get().unregisterLoadListener(this);
+        TemplateUrlServiceFactory.getForProfile(mProfile).unregisterLoadListener(this);
         mHasLoadObserver = false;
         refreshData();
     }
@@ -431,7 +434,7 @@
         mSelectedSearchEnginePosition = position;
 
         String keyword = toKeyword(mSelectedSearchEnginePosition);
-        TemplateUrlServiceFactory.get().setSearchEngine(keyword);
+        TemplateUrlServiceFactory.getForProfile(mProfile).setSearchEngine(keyword);
 
         // If the user has manually set the default search engine, disable auto switching.
         boolean manualSwitch = mSelectedSearchEnginePosition != mInitialEnginePosition;
@@ -450,8 +453,9 @@
             return "";
         }
 
-        String url = TemplateUrlServiceFactory.get().getSearchEngineUrlFromTemplateUrl(
-                templateUrl.getKeyword());
+        String url =
+                TemplateUrlServiceFactory.getForProfile(mProfile).getSearchEngineUrlFromTemplateUrl(
+                        templateUrl.getKeyword());
         if (url == null) {
             Log.e(TAG, "Invalid template URL found: %s", templateUrl);
             assert false;
@@ -467,8 +471,7 @@
 
         PermissionInfo locationSettings =
                 new PermissionInfo(ContentSettingsType.GEOLOCATION, url, null, false);
-        return locationSettings.getContentSetting(Profile.getLastUsedRegularProfile())
-                == ContentSettingValues.ALLOW;
+        return locationSettings.getContentSetting(mProfile) == ContentSettingValues.ALLOW;
     }
 
     private int computeStartIndexForRecentSearchEngines() {
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettings.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettings.java
index a6efb82d..b83645b 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettings.java
+++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettings.java
@@ -11,7 +11,9 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.ListFragment;
 
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.R;
+import org.chromium.chrome.browser.settings.ProfileDependentSetting;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 
 /**
@@ -21,8 +23,9 @@
  *
  * TODO(crbug.com/988877): Add on scroll shadow to action bar.
  */
-public class SearchEngineSettings extends ListFragment {
+public class SearchEngineSettings extends ListFragment implements ProfileDependentSetting {
     private SearchEngineAdapter mSearchEngineAdapter;
+    private Profile mProfile;
 
     @VisibleForTesting
     String getValueForTesting() {
@@ -88,6 +91,12 @@
 
     private void createAdapterIfNecessary() {
         if (mSearchEngineAdapter != null) return;
-        mSearchEngineAdapter = new SearchEngineAdapter(getActivity());
+        assert mProfile != null;
+        mSearchEngineAdapter = new SearchEngineAdapter(getActivity(), mProfile);
+    }
+
+    @Override
+    public void setProfile(Profile profile) {
+        mProfile = profile;
     }
 }
diff --git a/chrome/browser/sharing_hub/sharing_hub_features.cc b/chrome/browser/sharing_hub/sharing_hub_features.cc
index 8a3b38e..8f2719dd 100644
--- a/chrome/browser/sharing_hub/sharing_hub_features.cc
+++ b/chrome/browser/sharing_hub/sharing_hub_features.cc
@@ -16,17 +16,15 @@
 
 namespace {
 
-#if !BUILDFLAG(IS_CHROMEOS)
 // Whether the sharing hub feature should be disabled by policy.
 bool SharingHubDisabledByPolicy(content::BrowserContext* context) {
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
   const PrefService* prefs = Profile::FromBrowserContext(context)->GetPrefs();
   return !prefs->GetBoolean(prefs::kDesktopSharingHubEnabled);
 #else
   return false;
 #endif
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Whether screenshots-related features should be disabled by policy.
 // Currently used by desktop.
@@ -56,6 +54,12 @@
          !ScreenshotsDisabledByPolicy(context);
 }
 
+bool SharingIsDisabledByPolicy(content::BrowserContext* context) {
+  // TODO(ellyjones): should we separate out sharing hub from generic sharing?
+  // Or should we rename the policy to be more general?
+  return SharingHubDisabledByPolicy(context);
+}
+
 bool HasPageAction(content::BrowserContext* context, bool is_popup_mode) {
 #if BUILDFLAG(IS_CHROMEOS)
   return true;
diff --git a/chrome/browser/sharing_hub/sharing_hub_features.h b/chrome/browser/sharing_hub/sharing_hub_features.h
index 69271f16..3df8cbbe6 100644
--- a/chrome/browser/sharing_hub/sharing_hub_features.h
+++ b/chrome/browser/sharing_hub/sharing_hub_features.h
@@ -33,6 +33,10 @@
 // image editor before sharing.
 bool DesktopScreenshotsFeatureEnabled(content::BrowserContext* context);
 
+// Returns whether sharing is disabled altogether, in which case entry points
+// for sharing should be hidden.
+bool SharingIsDisabledByPolicy(content::BrowserContext* context);
+
 // Feature flag to enable the screenshots feature, currently accessed only
 // through the sharing hub.
 BASE_DECLARE_FEATURE(kDesktopScreenshots);
diff --git a/chrome/browser/signin/signin_features.cc b/chrome/browser/signin/signin_features.cc
index d26c0eb..e84c69e 100644
--- a/chrome/browser/signin/signin_features.cc
+++ b/chrome/browser/signin/signin_features.cc
@@ -31,16 +31,20 @@
     /*default_value=*/SigninPromoVariant::kSignIn,
     /*options=*/&kSignInPromoVariantOptions};
 
-// Feature that indicates that we should put the client in a study group to
-// be able to look at metrics in the long term. Does not affect the client's
-// behavior by itself, instead this is done through the `kForYouFre` feature.
-BASE_FEATURE(kForYouFreStudy,
-             "ForYouFreStudy",
+// Feature that indicates that we should put the client in a study group
+// (provided through `kForYouFreStudyGroup`) to be able to look at metrics in
+// the long term. Does not affect the client's behavior by itself, instead this
+// is done through the `kForYouFre` feature.
+BASE_FEATURE(kForYouFreSyntheticTrialRegistration,
+             "ForYouFreSyntheticTrialRegistration",
              base::FEATURE_DISABLED_BY_DEFAULT);
+
 // String that refers to the study group in which this install was enrolled.
-// Used to implement the sticky experiment tracking.
+// Used to implement the sticky experiment tracking. If the value is an empty
+// string, we don't register the client.
 const base::FeatureParam<std::string> kForYouFreStudyGroup{
-    &kForYouFreStudy, /*name=*/"group_name", /*default_value=*/""};
+    &kForYouFreSyntheticTrialRegistration, /*name=*/"group_name",
+    /*default_value=*/""};
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
 
diff --git a/chrome/browser/signin/signin_features.h b/chrome/browser/signin/signin_features.h
index 1c6f04a..6b86c40 100644
--- a/chrome/browser/signin/signin_features.h
+++ b/chrome/browser/signin/signin_features.h
@@ -19,7 +19,7 @@
 extern const base::FeatureParam<SigninPromoVariant>
     kForYouFreSignInPromoVariant;
 
-BASE_DECLARE_FEATURE(kForYouFreStudy);
+BASE_DECLARE_FEATURE(kForYouFreSyntheticTrialRegistration);
 
 extern const base::FeatureParam<std::string> kForYouFreStudyGroup;
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 7faa95e8..d3511bd 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2318,6 +2318,10 @@
       "ash/fwupd_download_client_impl.h",
       "ash/glanceables/chrome_glanceables_delegate.cc",
       "ash/glanceables/chrome_glanceables_delegate.h",
+      "ash/glanceables/glanceables_keyed_service.cc",
+      "ash/glanceables/glanceables_keyed_service.h",
+      "ash/glanceables/glanceables_keyed_service_factory.cc",
+      "ash/glanceables/glanceables_keyed_service_factory.h",
       "ash/global_media_controls/cast_media_notification_producer_keyed_service.cc",
       "ash/global_media_controls/cast_media_notification_producer_keyed_service.h",
       "ash/global_media_controls/cast_media_notification_producer_keyed_service_factory.cc",
@@ -3349,8 +3353,8 @@
       "//components/assist_ranker/proto",
       "//components/captive_portal/core",
       "//components/consent_auditor:consent_auditor",
+      "//components/content_relationship_verification",
       "//components/desks_storage",
-      "//components/digital_asset_links",
       "//components/endpoint_fetcher",
       "//components/exo",
       "//components/login",
@@ -3996,15 +4000,15 @@
       "views/chrome_cleaner_reboot_dialog_win.h",
       "views/critical_notification_bubble_view.cc",
       "views/critical_notification_bubble_view.h",
+      "views/frame/browser_caption_button_container_win.cc",
+      "views/frame/browser_caption_button_container_win.h",
       "views/frame/browser_desktop_window_tree_host.h",
       "views/frame/browser_desktop_window_tree_host_win.cc",
       "views/frame/browser_desktop_window_tree_host_win.h",
+      "views/frame/browser_frame_view_win.cc",
+      "views/frame/browser_frame_view_win.h",
       "views/frame/browser_window_property_manager_win.cc",
       "views/frame/browser_window_property_manager_win.h",
-      "views/frame/glass_browser_caption_button_container.cc",
-      "views/frame/glass_browser_caption_button_container.h",
-      "views/frame/glass_browser_frame_view.cc",
-      "views/frame/glass_browser_frame_view.h",
       "views/frame/minimize_button_metrics_win.cc",
       "views/frame/minimize_button_metrics_win.h",
       "views/frame/native_browser_frame_factory_aura.cc",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java
index c038242..a09cbbe 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java
@@ -105,18 +105,18 @@
         final List<ListItem> modelList = new ArrayList<>(pedalsCount);
 
         for (OmniboxPedal chip : omniboxPedalList) {
-            final var chipModel = new PropertyModel.Builder(ChipProperties.ALL_KEYS)
-                                          .with(ChipProperties.TEXT, chip.getHint())
-                                          .build();
-
-            // Apply changes manually to investigate bizarre CQ finding.
             final var chipIcon = mOmniboxPedalDelegate.getIcon(chip);
-            chipModel.set(ChipProperties.CONTENT_DESCRIPTION,
-                    mContext.getString(R.string.accessibility_omnibox_pedal, chip.getHint()));
-            chipModel.set(ChipProperties.ENABLED, true);
-            chipModel.set(ChipProperties.CLICK_HANDLER, m -> executeAction(chip, position));
-            chipModel.set(ChipProperties.ICON, chipIcon.iconRes);
-            chipModel.set(ChipProperties.APPLY_ICON_TINT, chipIcon.tintWithTextColor);
+            final var chipModel =
+                    new PropertyModel.Builder(ChipProperties.ALL_KEYS)
+                            .with(ChipProperties.TEXT, chip.getHint())
+                            .with(ChipProperties.CONTENT_DESCRIPTION,
+                                    mContext.getString(
+                                            R.string.accessibility_omnibox_pedal, chip.getHint()))
+                            .with(ChipProperties.ENABLED, true)
+                            .with(ChipProperties.CLICK_HANDLER, m -> executeAction(chip, position))
+                            .with(ChipProperties.ICON, chipIcon.iconRes)
+                            .with(ChipProperties.APPLY_ICON_TINT, chipIcon.tintWithTextColor)
+                            .build();
 
             modelList.add(
                     new ListItem(PedalSuggestionViewProperties.ViewType.PEDAL_VIEW, chipModel));
diff --git a/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc b/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc
index 822e8b4c..21dc7d3 100644
--- a/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc
+++ b/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.h"
 
-#include "ash/constants/app_types.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/public/cpp/desk_template.h"
 #include "ash/public/cpp/system/toast_data.h"
@@ -13,7 +11,6 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "base/check.h"
 #include "base/functional/bind.h"
-#include "base/strings/string_number_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -29,28 +26,22 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/desks/chrome_desks_util.h"
 #include "chrome/browser/ui/ash/desks/desks_client.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/app_constants/constants.h"
 #include "components/app_restore/app_launch_info.h"
 #include "components/app_restore/app_restore_data.h"
-#include "components/app_restore/app_restore_utils.h"
 #include "components/app_restore/full_restore_save_handler.h"
 #include "components/app_restore/full_restore_utils.h"
 #include "components/app_restore/restore_data.h"
 #include "components/app_restore/window_properties.h"
 #include "components/favicon/core/favicon_service.h"
-#include "components/favicon_base/favicon_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/intent.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "components/user_manager/user_manager.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -58,7 +49,6 @@
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/paint_vector_icon.h"
 #include "url/gurl.h"
 
 namespace {
@@ -167,22 +157,23 @@
   ash::ToastManager::Get()->Show(std::move(toast_data));
 }
 
-// Creates a callback for when a favicon image is retrieved which creates a
-// standard icon image and then calls `callback` with the standardized image.
+// Creates a standard icon image via `result`, and then calls `callback` with
+// the standardized image.
 // TODO(crbug.com/1318250): Remove this once non-lacros browser is not
 // supported.
-base::OnceCallback<void(const favicon_base::FaviconImageResult&)>
-ImageResultToImageSkia(
-    base::OnceCallback<void(const gfx::ImageSkia&)> callback) {
-  return base::BindOnce(
-      [](base::OnceCallback<void(const gfx::ImageSkia&)> image_skia_callback,
-         const favicon_base::FaviconImageResult& result) {
-        auto image = result.image.AsImageSkia();
-        image.EnsureRepsForSupportedScales();
-        std::move(image_skia_callback)
-            .Run(apps::CreateStandardIconImage(image));
-      },
-      std::move(callback));
+void ImageResultToImageSkia(
+    base::OnceCallback<void(const gfx::ImageSkia&)> callback,
+    const favicon_base::FaviconRawBitmapResult& result) {
+  if (!result.is_valid()) {
+    std::move(callback).Run(gfx::ImageSkia());
+    return;
+  }
+
+  auto image = gfx::Image::CreateFrom1xPNGBytes(result.bitmap_data->front(),
+                                                result.bitmap_data->size())
+                   .AsImageSkia();
+  image.EnsureRepsForSupportedScales();
+  std::move(callback).Run(apps::CreateStandardIconImage(image));
 }
 
 // Creates a callback for when a app icon image is retrieved which creates a
@@ -378,8 +369,10 @@
           ProfileManager::GetActiveUserProfile(),
           ServiceAccessType::EXPLICIT_ACCESS);
 
-  favicon_service->GetFaviconImageForPageURL(
-      GURL(page_url), ImageResultToImageSkia(std::move(callback)), tracker);
+  favicon_service->GetRawFaviconForPageURL(
+      GURL(page_url), {favicon_base::IconType::kFavicon}, 0,
+      /*fallback_to_host=*/false,
+      base::BindOnce(&ImageResultToImageSkia, std::move(callback)), tracker);
 }
 
 void ChromeSavedDeskDelegate::GetIconForAppId(
diff --git a/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.h b/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.h
index b2b8448e..6a821491 100644
--- a/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.h
+++ b/chrome/browser/ui/ash/desks/chrome_saved_desk_delegate.h
@@ -11,8 +11,10 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/crosapi/mojom/desk_template.mojom-forward.h"
-#include "components/favicon_base/favicon_types.h"
-#include "components/services/app_service/public/cpp/icon_types.h"
+
+namespace aura {
+class Window;
+}  // namespace aura
 
 namespace base {
 class CancelableTaskTracker;
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_keyed_service.cc b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service.cc
new file mode 100644
index 0000000..af3f3e2
--- /dev/null
+++ b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service.cc
@@ -0,0 +1,15 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service.h"
+
+namespace ash {
+
+GlanceablesKeyedService::GlanceablesKeyedService() = default;
+
+GlanceablesKeyedService::~GlanceablesKeyedService() = default;
+
+void GlanceablesKeyedService::Shutdown() {}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_keyed_service.h b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service.h
new file mode 100644
index 0000000..f02347a
--- /dev/null
+++ b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service.h
@@ -0,0 +1,28 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_GLANCEABLES_GLANCEABLES_KEYED_SERVICE_H_
+#define CHROME_BROWSER_UI_ASH_GLANCEABLES_GLANCEABLES_KEYED_SERVICE_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace ash {
+
+// Browser context keyed service that owns implementations of interfaces from
+// ash/ needed to communicate with different Google services as part of
+// Glanceables project.
+class GlanceablesKeyedService : public KeyedService {
+ public:
+  GlanceablesKeyedService();
+  GlanceablesKeyedService(const GlanceablesKeyedService&) = delete;
+  GlanceablesKeyedService& operator=(const GlanceablesKeyedService&) = delete;
+  ~GlanceablesKeyedService() override;
+
+  // KeyedService:
+  void Shutdown() override;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_UI_ASH_GLANCEABLES_GLANCEABLES_KEYED_SERVICE_H_
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.cc b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.cc
new file mode 100644
index 0000000..09229d0
--- /dev/null
+++ b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.cc
@@ -0,0 +1,40 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h"
+
+#include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_selections.h"
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service.h"
+#include "content/public/browser/browser_context.h"
+
+namespace ash {
+
+// static
+GlanceablesKeyedServiceFactory* GlanceablesKeyedServiceFactory::GetInstance() {
+  static base::NoDestructor<GlanceablesKeyedServiceFactory> factory;
+  return factory.get();
+}
+
+GlanceablesKeyedServiceFactory::GlanceablesKeyedServiceFactory()
+    : ProfileKeyedServiceFactory("GlanceablesKeyedService",
+                                 ProfileSelections::BuildForRegularProfile()) {}
+
+GlanceablesKeyedService* GlanceablesKeyedServiceFactory::GetService(
+    content::BrowserContext* context) {
+  return static_cast<GlanceablesKeyedService*>(
+      GetInstance()->GetServiceForBrowserContext(
+          context, /*create=*/features::AreGlanceablesV2Enabled()));
+}
+
+std::unique_ptr<KeyedService>
+GlanceablesKeyedServiceFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+  return std::make_unique<GlanceablesKeyedService>();
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h
new file mode 100644
index 0000000..721f2485
--- /dev/null
+++ b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_GLANCEABLES_GLANCEABLES_KEYED_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_UI_ASH_GLANCEABLES_GLANCEABLES_KEYED_SERVICE_FACTORY_H_
+
+#include <memory>
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace ash {
+
+class GlanceablesKeyedService;
+
+// Factory class for `GlanceablesKeyedService`. Creates instances of that
+// service for regular profiles only.
+class GlanceablesKeyedServiceFactory : public ProfileKeyedServiceFactory {
+ public:
+  GlanceablesKeyedServiceFactory(const GlanceablesKeyedServiceFactory&) =
+      delete;
+  GlanceablesKeyedServiceFactory& operator=(
+      const GlanceablesKeyedServiceFactory&) = delete;
+
+  static GlanceablesKeyedServiceFactory* GetInstance();
+
+  GlanceablesKeyedService* GetService(content::BrowserContext* context);
+
+ protected:
+  // BrowserContextKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+
+ private:
+  friend base::NoDestructor<GlanceablesKeyedServiceFactory>;
+  GlanceablesKeyedServiceFactory();
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_UI_ASH_GLANCEABLES_GLANCEABLES_KEYED_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory_unittest.cc b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory_unittest.cc
new file mode 100644
index 0000000..24b205e6
--- /dev/null
+++ b/chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service_factory.h"
+
+#include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ui/ash/glanceables/glanceables_keyed_service.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+class GlanceablesKeyedServiceFactoryTest : public BrowserWithTestWindowTest {
+ public:
+  GlanceablesKeyedServiceFactoryTest()
+      : user_manager_(std::make_unique<FakeChromeUserManager>()) {}
+
+  TestingProfileManager* profile_manager() {
+    return BrowserWithTestWindowTest::profile_manager();
+  }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_{features::kGlanceablesV2};
+  std::unique_ptr<FakeChromeUserManager> user_manager_;
+};
+
+TEST_F(GlanceablesKeyedServiceFactoryTest, NoSupportWhenFeatureIsDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kGlanceablesV2);
+
+  EXPECT_FALSE(
+      GlanceablesKeyedServiceFactory::GetInstance()->GetService(GetProfile()));
+}
+
+TEST_F(GlanceablesKeyedServiceFactoryTest, NoSupportForGuestProfile) {
+  std::unique_ptr<TestingProfile> guest_profile =
+      TestingProfile::Builder()
+          .SetGuestSession()
+          .SetProfileName("guest_profile")
+          .AddTestingFactories({})
+          .Build();
+  ASSERT_TRUE(guest_profile);
+
+  auto* service = GlanceablesKeyedServiceFactory::GetInstance()->GetService(
+      guest_profile.get());
+  EXPECT_FALSE(service);
+}
+
+TEST_F(GlanceablesKeyedServiceFactoryTest, NoSupportForOffTheRecordProfile) {
+  auto* service_for_primary_profile =
+      GlanceablesKeyedServiceFactory::GetInstance()->GetService(GetProfile());
+  EXPECT_TRUE(service_for_primary_profile);
+
+  TestingProfile* incognito_profile =
+      TestingProfile::Builder()
+          .SetProfileName(GetProfile()->GetProfileUserName())
+          .BuildIncognito(GetProfile());
+  ASSERT_TRUE(incognito_profile);
+  ASSERT_TRUE(incognito_profile->IsOffTheRecord());
+
+  auto* service_for_incognito_profile =
+      GlanceablesKeyedServiceFactory::GetInstance()->GetService(
+          incognito_profile);
+  EXPECT_FALSE(service_for_incognito_profile);
+}
+
+TEST_F(GlanceablesKeyedServiceFactoryTest, SupportsMultipleUserProfiles) {
+  auto* service_1 =
+      GlanceablesKeyedServiceFactory::GetInstance()->GetService(GetProfile());
+
+  // Returns the same instance for the same profile.
+  EXPECT_EQ(
+      service_1,
+      GlanceablesKeyedServiceFactory::GetInstance()->GetService(GetProfile()));
+
+  const std::string second_profile_name = "second_profile";
+  const AccountId account_id(AccountId::FromUserEmail(second_profile_name));
+  user_manager_->AddUser(account_id);
+  user_manager_->LoginUser(account_id);
+  TestingProfile* second_profile = profile_manager()->CreateTestingProfile(
+      second_profile_name,
+      std::unique_ptr<sync_preferences::PrefServiceSyncable>(),
+      u"Test profile 2",
+      /*avatar_id=*/1,
+      /*testing_factories=*/{});
+
+  // Returns different instances for different profiles.
+  EXPECT_NE(service_1,
+            GlanceablesKeyedServiceFactory::GetInstance()->GetService(
+                second_profile));
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
index 398c8415..3c5b06b 100644
--- a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
@@ -9,7 +9,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/projector/annotator_tool.h"
-#include "ash/webui/projector_app/annotator_message_handler.h"
+#include "ash/webui/projector_app/annotator_page_handler_impl.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "base/functional/bind.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -155,26 +155,26 @@
                                std::move(callback));
 }
 
-void ProjectorAppClientImpl::SetAnnotatorMessageHandler(
-    ash::AnnotatorMessageHandler* handler) {
-  annotator_message_handler_ = handler;
+void ProjectorAppClientImpl::SetAnnotatorPageHandler(
+    ash::AnnotatorPageHandlerImpl* handler) {
+  annotator_handler_ = handler;
 }
 
-void ProjectorAppClientImpl::ResetAnnotatorMessageHandler(
-    ash::AnnotatorMessageHandler* handler) {
-  if (annotator_message_handler_ == handler) {
-    annotator_message_handler_ = nullptr;
+void ProjectorAppClientImpl::ResetAnnotatorPageHandler(
+    ash::AnnotatorPageHandlerImpl* handler) {
+  if (annotator_handler_ == handler) {
+    annotator_handler_ = nullptr;
   }
 }
 
 void ProjectorAppClientImpl::SetTool(const ash::AnnotatorTool& tool) {
-  DCHECK(annotator_message_handler_);
-  annotator_message_handler_->SetTool(tool);
+  DCHECK(annotator_handler_);
+  annotator_handler_->SetTool(tool);
 }
 
 void ProjectorAppClientImpl::Clear() {
-  DCHECK(annotator_message_handler_);
-  annotator_message_handler_->Clear();
+  DCHECK(annotator_handler_);
+  annotator_handler_->Clear();
 }
 
 void ProjectorAppClientImpl::NotifyAppUIActive(bool active) {
diff --git a/chrome/browser/ui/ash/projector/projector_app_client_impl.h b/chrome/browser/ui/ash/projector/projector_app_client_impl.h
index ffa0731..a25a270 100644
--- a/chrome/browser/ui/ash/projector/projector_app_client_impl.h
+++ b/chrome/browser/ui/ash/projector/projector_app_client_impl.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/projector_app/annotator_message_handler.h"
+#include "ash/webui/projector_app/annotator_page_handler_impl.h"
 #include "ash/webui/projector_app/projector_app_client.h"
 #include "base/observer_list.h"
 #include "chrome/browser/ui/ash/projector/pending_screencast_manager.h"
@@ -24,10 +24,6 @@
 class PrefRegistrySyncable;
 }  // namespace user_prefs
 
-namespace ash {
-class AnnotatorMessageHandler;
-}  // namespace ash
-
 // Implements the interface for Projector App.
 class ProjectorAppClientImpl : public ash::ProjectorAppClient {
  public:
@@ -56,10 +52,9 @@
       const std::string& video_file_id,
       const std::string& resource_key,
       ash::ProjectorAppClient::OnGetVideoCallback callback) const override;
-  void SetAnnotatorMessageHandler(
-      ash::AnnotatorMessageHandler* handler) override;
-  void ResetAnnotatorMessageHandler(
-      ash::AnnotatorMessageHandler* handler) override;
+  void SetAnnotatorPageHandler(ash::AnnotatorPageHandlerImpl* handler) override;
+  void ResetAnnotatorPageHandler(
+      ash::AnnotatorPageHandlerImpl* handler) override;
   void SetTool(const ash::AnnotatorTool& tool) override;
   void Clear() override;
   void NotifyAppUIActive(bool active) override;
@@ -67,8 +62,8 @@
       const std::vector<base::FilePath>& screencast_paths,
       bool suppress) override;
 
-  ash::AnnotatorMessageHandler* get_annotator_message_handler_for_test() {
-    return annotator_message_handler_;
+  ash::AnnotatorPageHandlerImpl* get_annotator_handler_for_test() {
+    return annotator_handler_;
   }
   PendingScreencastManager* get_pending_screencast_manager_for_test() {
     return &pending_screencast_manager_;
@@ -85,7 +80,7 @@
 
   ash::ScreencastManager screencast_manager_;
 
-  ash::AnnotatorMessageHandler* annotator_message_handler_ = nullptr;
+  ash::AnnotatorPageHandlerImpl* annotator_handler_ = nullptr;
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_PROJECTOR_PROJECTOR_APP_CLIENT_IMPL_H_
diff --git a/chrome/browser/ui/autofill/autofill_context_menu_manager_unittest.cc b/chrome/browser/ui/autofill/autofill_context_menu_manager_unittest.cc
index 7ae98b87..a73044b 100644
--- a/chrome/browser/ui/autofill/autofill_context_menu_manager_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_context_menu_manager_unittest.cc
@@ -153,7 +153,7 @@
 };
 
 // Tests that the Autofill context menu is correctly set up.
-TEST_F(AutofillContextMenuManagerTest, AutofillContextMenuContents) {
+TEST_F(AutofillContextMenuManagerTest, DISABLED_AutofillContextMenuContents) {
   autofill_context_menu_manager()->AppendItems();
   std::vector<std::u16string> all_added_strings;
 
@@ -260,7 +260,7 @@
 // For all the command ids that are used to set up the context menu, initiating
 // filling for each one of them results in the call to
 // `RendererShouldFillFieldWithValue`.
-TEST_F(AutofillContextMenuManagerTest, ExecuteCommand) {
+TEST_F(AutofillContextMenuManagerTest, DISABLED_ExecuteCommand) {
   DCHECK(driver());
   autofill_context_menu_manager()->AppendItems();
   auto mapper = autofill_context_menu_manager()
@@ -289,7 +289,8 @@
 
 // Tests that the Autofill's ContentAutofillDriver is called to record metrics
 // when the context menu is triggered on a field.
-TEST_F(AutofillContextMenuManagerTest, RecordContextMenuIsShownOnField) {
+TEST_F(AutofillContextMenuManagerTest,
+       DISABLED_RecordContextMenuIsShownOnField) {
   FormRendererId form_renderer_id(test::MakeFormRendererId());
   FieldRendererId field_renderer_id(test::MakeFieldRendererId());
   autofill_context_menu_manager()->set_params_for_testing(
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller.h b/chrome/browser/ui/autofill/payments/save_card_bubble_controller.h
index 1fe849e..7036eb0 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller.h
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller.h
@@ -22,7 +22,6 @@
 namespace autofill {
 
 class CreditCard;
-class AutofillBubbleBase;
 enum class BubbleType;
 
 // Interface that exposes controller functionality to save card bubbles.
@@ -64,10 +63,6 @@
   // Returns the card that will be uploaded if the user accepts.
   virtual const CreditCard& GetCard() const = 0;
 
-  // Returns the currently active save card bubble view. Can be nullptr if no
-  // bubble is visible.
-  virtual AutofillBubbleBase* GetSaveCardBubbleView() const = 0;
-
   // Returns whether the dialog should include a textfield requesting the user
   // to confirm/provide cardholder name.
   virtual bool ShouldRequestNameFromUser() const = 0;
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
index 98e1f9b..c1dccd0 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
@@ -259,16 +259,6 @@
   return card_;
 }
 
-AutofillBubbleBase* SaveCardBubbleControllerImpl::GetSaveCardBubbleView()
-    const {
-  return bubble_view();
-}
-
-SavePaymentIconController::PaymentBubbleType
-SaveCardBubbleControllerImpl::GetPaymentBubbleType() const {
-  return PaymentBubbleType::kCreditCard;
-}
-
 bool SaveCardBubbleControllerImpl::ShouldRequestNameFromUser() const {
   return options_.should_request_name_from_user;
 }
@@ -471,7 +461,12 @@
 }
 
 AutofillBubbleBase* SaveCardBubbleControllerImpl::GetPaymentBubbleView() const {
-  return GetSaveCardBubbleView();
+  return bubble_view();
+}
+
+SavePaymentIconController::PaymentBubbleType
+SaveCardBubbleControllerImpl::GetPaymentBubbleType() const {
+  return PaymentBubbleType::kCreditCard;
 }
 
 PageActionIconType SaveCardBubbleControllerImpl::GetPageActionIconType() {
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
index d416b487..2a368cb 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
@@ -118,7 +118,6 @@
   const AccountInfo& GetAccountInfo() override;
   Profile* GetProfile() const override;
   const CreditCard& GetCard() const override;
-  AutofillBubbleBase* GetSaveCardBubbleView() const override;
   bool ShouldRequestNameFromUser() const override;
   bool ShouldRequestExpirationDateFromUser() const override;
 
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
index 8109fd1a..29c42e6 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
@@ -170,14 +170,14 @@
 // Tests that opening a new tab will hide the save card bubble.
 IN_PROC_BROWSER_TEST_F(SaveCardBubbleControllerImplTest, NewTabHidesDialog) {
   ShowUi("Local");
-  EXPECT_NE(nullptr, controller()->GetSaveCardBubbleView());
+  EXPECT_NE(nullptr, controller()->GetPaymentBubbleView());
   // Open a new tab page in the foreground.
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), GURL(chrome::kChromeUINewTabURL),
       WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
           ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  EXPECT_EQ(nullptr, controller()->GetSaveCardBubbleView());
+  EXPECT_EQ(nullptr, controller()->GetPaymentBubbleView());
 }
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
index e0d0f2de..50bc11d6 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
@@ -498,7 +498,7 @@
   // After closing the sign-in promo, clicking the icon should bring
   // up the Manage cards bubble.
   EXPECT_EQ(BubbleType::MANAGE_CARDS, controller()->GetBubbleType());
-  EXPECT_NE(nullptr, controller()->GetSaveCardBubbleView());
+  EXPECT_NE(nullptr, controller()->GetPaymentBubbleView());
 }
 
 TEST_F(SaveCardBubbleControllerImplTest,
@@ -570,7 +570,7 @@
   // Icon should disappear after an upload save,
   // even when this flag is enabled.
   EXPECT_FALSE(controller()->IsIconVisible());
-  EXPECT_EQ(nullptr, controller()->GetSaveCardBubbleView());
+  EXPECT_EQ(nullptr, controller()->GetPaymentBubbleView());
 }
 
 TEST_F(SaveCardBubbleControllerImplTest,
diff --git a/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc b/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc
index 3bf775a4..d6be84f 100644
--- a/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc
+++ b/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc
@@ -37,7 +37,7 @@
   return original_user_gesture_;
 }
 
-const GURL& ChromePopupNavigationDelegate::GetURL() {
+GURL ChromePopupNavigationDelegate::GetURL() {
   return params_.url;
 }
 
diff --git a/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h b/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h
index f98d11d..a9f63d4fb 100644
--- a/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h
+++ b/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h
@@ -16,7 +16,7 @@
   // blocked_content::PopupNavigationDelegate:
   content::RenderFrameHost* GetOpener() override;
   bool GetOriginalUserGesture() override;
-  const GURL& GetURL() override;
+  GURL GetURL() override;
   NavigateResult NavigateWithGesture(
       const blink::mojom::WindowFeatures& window_features,
       absl::optional<WindowOpenDisposition> updated_disposition) override;
diff --git a/chrome/browser/ui/location_bar/location_bar.h b/chrome/browser/ui/location_bar/location_bar.h
index ed7124e..23299773 100644
--- a/chrome/browser/ui/location_bar/location_bar.h
+++ b/chrome/browser/ui/location_bar/location_bar.h
@@ -33,14 +33,6 @@
   virtual base::TimeTicks GetMatchSelectionTimestamp() const = 0;
   virtual bool IsInputTypedUrlWithoutScheme() const = 0;
 
-  // Accepts the current string of text entered in the location bar.
-  virtual void AcceptInput() = 0;
-
-  // Accepts the current string of text entered in the location bar. If
-  // |match_selection_timestamp| is not null, uses this value to track
-  // latency of page loads starting at user input.
-  virtual void AcceptInput(base::TimeTicks match_selection_timestamp) = 0;
-
   // Focuses the location bar. User-initiated focuses (like pressing Ctrl+L)
   // should have |is_user_initiated| set to true. In those cases, we want to
   // take some extra steps, like selecting everything and maybe uneliding.
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index 3e46b69bc..9c7c61e 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -70,7 +70,12 @@
   void PressEnterAndWaitForLoadStop() {
     content::TestNavigationObserver observer(
         browser()->tab_strip_model()->GetActiveWebContents());
-    browser()->window()->GetLocationBar()->AcceptInput();
+    browser()
+        ->window()
+        ->GetLocationBar()
+        ->GetOmniboxView()
+        ->model()
+        ->OpenSelection();
     observer.Wait();
   }
 
diff --git a/chrome/browser/ui/startup/first_run_service.cc b/chrome/browser/ui/startup/first_run_service.cc
index 47a8efc..9d59da1a 100644
--- a/chrome/browser/ui/startup/first_run_service.cc
+++ b/chrome/browser/ui/startup/first_run_service.cc
@@ -453,7 +453,7 @@
   }
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
-  if (base::FeatureList::IsEnabled(kForYouFreStudy)) {
+  if (base::FeatureList::IsEnabled(kForYouFreSyntheticTrialRegistration)) {
     // We use this point to register for the study as it can give us a good
     // counterfactual, before checking the state of the feature itself. The
     // service is created on demand so we are in a code path that will require
diff --git a/chrome/browser/ui/startup/first_run_service_browsertest.cc b/chrome/browser/ui/startup/first_run_service_browsertest.cc
index 8bc9022..f937c63 100644
--- a/chrome/browser/ui/startup/first_run_service_browsertest.cc
+++ b/chrome/browser/ui/startup/first_run_service_browsertest.cc
@@ -408,7 +408,8 @@
 
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {
-            {kForYouFreStudy, {{"group_name", kStudyTestGroupName1}}},
+            {kForYouFreSyntheticTrialRegistration,
+             {{"group_name", kStudyTestGroupName1}}},
             {kForYouFre, {}},
         },
         {});
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
index ce8784d..320c61b 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
@@ -84,7 +84,6 @@
 
 void SavedTabGroupBrowserListener::StopTrackingWebContents(
     content::WebContents* web_contents) {
-  CHECK(web_contents_to_tab_id_map_.count(web_contents) > 0);
   web_contents_to_tab_id_map_.erase(web_contents);
 }
 
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
index 4cbf90b..13843fd 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
@@ -51,8 +51,7 @@
   base::Token GetOrCreateTrackedIDForWebContents(
       content::WebContents* web_contents);
 
-  // Stops tracking the webcontents for changes. CHECKS if not currently
-  // tracked.
+  // Stops tracking the webcontents for changes.
   void StopTrackingWebContents(content::WebContents* web_contents);
 
   // TabStripModelObserver:
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
index f977517..1f6da8f2 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
@@ -157,14 +157,6 @@
 // and severity when an upgrade is detected.
 TEST_P(AppMenuIconControllerTest, UpgradeNotification) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Forcibly enable Lacros Profile migration, so that IDC_LACROS_DATA_MIGRATION
-  // becomes visible. Note that profile migration is only enabled if Lacros is
-  // the only browser.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures(
-      {ash::features::kLacrosSupport, ash::features::kLacrosPrimary,
-       ash::features::kLacrosOnly},
-      {});
   auto set_lacros_enabled =
       crosapi::browser_util::SetLacrosEnabledForTest(true);
 #endif
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 7e3f12e..a2c89853 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -160,14 +160,6 @@
   EXPECT_TRUE(detector->notify_upgrade());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Forcibly enable Lacros Profile migration, so that IDC_LACROS_DATA_MIGRATION
-  // becomes visible. Note that profile migration is only enabled if Lacros is
-  // the only browser.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures(
-      {ash::features::kLacrosSupport, ash::features::kLacrosPrimary,
-       ash::features::kLacrosOnly},
-      {});
   auto set_lacros_enabled =
       crosapi::browser_util::SetLacrosEnabledForTest(true);
 #endif
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 3c12398..1c257eb2 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -7,7 +7,6 @@
 #include "base/feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "ui_features.h"
 
 namespace features {
 
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index f67b645..19a19db 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -699,12 +699,13 @@
   }
 
   SaveCardBubbleViews* GetSaveCardBubbleViews() {
-    SaveCardBubbleController* save_card_bubble_controller =
-        SaveCardBubbleController::Get(GetActiveWebContents());
+    SaveCardBubbleControllerImpl::CreateForWebContents(GetActiveWebContents());
+    SaveCardBubbleControllerImpl* save_card_bubble_controller =
+        SaveCardBubbleControllerImpl::FromWebContents(GetActiveWebContents());
     if (!save_card_bubble_controller)
       return nullptr;
     AutofillBubbleBase* save_card_bubble_view =
-        save_card_bubble_controller->GetSaveCardBubbleView();
+        save_card_bubble_controller->GetPaymentBubbleView();
     if (!save_card_bubble_view)
       return nullptr;
     return static_cast<SaveCardBubbleViews*>(save_card_bubble_view);
diff --git a/chrome/browser/ui/views/autofill/popup/popup_cell_view.cc b/chrome/browser/ui/views/autofill/popup/popup_cell_view.cc
index 40cc7841..28efeb6e 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_cell_view.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_cell_view.cc
@@ -99,18 +99,37 @@
 }
 
 void PopupCellView::OnMouseEntered(const ui::MouseEvent& event) {
+  // `OnMouseEntered()` does not imply that the mouse had been outside of the
+  // item's bounds before: `OnMouseEntered()` fires if the mouse moves just
+  // a little bit on the item. We don't want to show a preview in such a case.
+  if (!mouse_observed_outside_item_bounds_) {
+    return;
+  }
+
   if (on_entered_callback_) {
     on_entered_callback_.Run();
   }
 }
 
 void PopupCellView::OnMouseExited(const ui::MouseEvent& event) {
+  // `OnMouseExited()` does not imply that the mouse has left the item's screen
+  // bounds: `OnMouseExited()` fires (on Windows, at least) when another popup
+  // overlays this item and the mouse is above the new popup
+  // (crbug.com/1287364).
+  mouse_observed_outside_item_bounds_ |= !IsMouseInsideItemBounds();
+
   if (on_exited_callback_) {
     on_exited_callback_.Run();
   }
 }
 
 void PopupCellView::OnMouseReleased(const ui::MouseEvent& event) {
+  // Ignore mouse clicks unless the user made the explicit choice to selected
+  // the current item.
+  if (!mouse_observed_outside_item_bounds_) {
+    return;
+  }
+
   if (on_accepted_callback_ && event.IsOnlyLeftMouseButton() &&
       HitTestPoint(event.location())) {
     on_accepted_callback_.Run();
@@ -154,6 +173,11 @@
   }
 }
 
+void PopupCellView::OnPaint(gfx::Canvas* canvas) {
+  views::View::OnPaint(canvas);
+  mouse_observed_outside_item_bounds_ |= !IsMouseInsideItemBounds();
+}
+
 void PopupCellView::RefreshStyle() {
   ui::ColorId kBackgroundColorId = GetSelected()
                                        ? ui::kColorDropdownBackgroundSelected
diff --git a/chrome/browser/ui/views/autofill/popup/popup_cell_view.h b/chrome/browser/ui/views/autofill/popup/popup_cell_view.h
index 93c7aed..ce9086c 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_cell_view.h
+++ b/chrome/browser/ui/views/autofill/popup/popup_cell_view.h
@@ -109,6 +109,8 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
 
+  void OnPaint(gfx::Canvas* canvas) override;
+
  private:
   // Returns true if the mouse is within the bounds of this item. This is not
   // affected by whether or not the item is overlaid by another popup.
@@ -132,6 +134,18 @@
 
   // The labels whose style is updated when the cell's selection status changes.
   std::vector<raw_ptr<views::Label>> tracked_labels_;
+
+  // We want a mouse click to accept a suggestion only if the user has made an
+  // explicit choice. Therefore, we shall ignore mouse clicks unless the mouse
+  // has been moved into the item's screen bounds. For example, if the item is
+  // hovered by the mouse at the time it's first shown, we want to ignore clicks
+  // until the mouse has left and re-entered the bounds of the item
+  // (crbug.com/1240472, crbug.com/1241585, crbug.com/1287364).
+  // This is particularly relevant because mouse click interactions may be
+  // processed with a delay, making it seem as if the two click interactions of
+  // a double click were executed at intervals larger than the threshold (500ms)
+  // checked in the controller (crbug.com/1418837).
+  bool mouse_observed_outside_item_bounds_ = false;
 };
 
 BEGIN_VIEW_BUILDER(/* no export*/, PopupCellView, views::View)
diff --git a/chrome/browser/ui/views/autofill/popup/popup_cell_view_unittest.cc b/chrome/browser/ui/views/autofill/popup/popup_cell_view_unittest.cc
index 4307875..9547ee6b 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_cell_view_unittest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_cell_view_unittest.cc
@@ -230,4 +230,30 @@
 }
 #endif  // !BUILDFLAG(IS_MAC)
 
+TEST_F(PopupCellViewTest, IgnoreClickIfMouseWasNotOutsideBefore) {
+  std::unique_ptr<PopupCellView> cell =
+      views::Builder<PopupCellView>()
+          .SetAccessibilityDelegate(
+              std::make_unique<TestAccessibilityDelegate>())
+          .Build();
+  views::Label* label =
+      cell->AddChildView(std::make_unique<views::Label>(u"Label text"));
+  ShowView(std::move(cell));
+
+  StrictMock<base::MockCallback<base::RepeatingClosure>> accept_callback;
+
+  view().SetOnAcceptedCallback(accept_callback.Get());
+  generator().MoveMouseTo(label->GetBoundsInScreen().CenterPoint());
+  Paint();
+  // No OnAccept callback is run.
+  generator().ClickLeftButton();
+
+  generator().MoveMouseTo(kOutOfBounds);
+  Paint();
+  generator().MoveMouseTo(label->GetBoundsInScreen().CenterPoint());
+  // If the mouse has been outside before, the accept click is passed through.
+  EXPECT_CALL(accept_callback, Run);
+  generator().ClickLeftButton();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
index 47c0b74..9bdd2c2 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
@@ -628,6 +628,39 @@
   view().RemoveAllChildViews();
 }
 
+// Tests that if the mouse hovers a suggestion when the popup is displayed,
+// clicking the suggestion triggers no AcceptSuggestion() event.
+TEST_P(PopupViewViewsTestWithClickablePopupItemId,
+       IgnoreClickIfFocusedAtPaintWithoutExit) {
+  CreateAndShowView({popup_item_id()});
+  EXPECT_CALL(controller(), AcceptSuggestion).Times(0);
+  generator().MoveMouseTo(GetCenterOfSuggestion(0));
+  ASSERT_TRUE(view().IsMouseHovered());
+  Paint();
+  generator().ClickLeftButton();
+  view().RemoveAllChildViews();
+}
+
+// Tests that if the mouse hovers a suggestion when the popup is displayed and
+// moves around on this suggestion, clicking the suggestion triggers no
+// AcceptSuggestion() event.
+TEST_P(PopupViewViewsTestWithClickablePopupItemId,
+       IgnoreClickIfFocusedAtPaintWithSlightMouseMovement) {
+  CreateAndShowView({popup_item_id()});
+  EXPECT_CALL(controller(), AcceptSuggestion).Times(0);
+  int width = GetRowViewAt(0).width();
+  int height = GetRowViewAt(0).height();
+  for (int x : {-width / 3, width / 3}) {
+    for (int y : {-height / 3, height / 3}) {
+      generator().MoveMouseTo(GetCenterOfSuggestion(0) + gfx::Vector2d(x, y));
+      ASSERT_TRUE(view().IsMouseHovered());
+      Paint();
+    }
+  }
+  generator().ClickLeftButton();
+  view().RemoveAllChildViews();
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          PopupViewViewsTestWithAnyPopupItemId,
                          testing::ValuesIn([] {
diff --git a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc
index 37c7ac0..d240768 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc
@@ -21,13 +21,18 @@
 
 namespace {
 
-const std::u16string& GetMostRecentlyModifiedUserBookmarkFolderName(
-    Profile* profile) {
+const std::u16string& GetBookmarkParentNameOrDefault(Profile* profile,
+                                                     const GURL& url) {
   bookmarks::BookmarkModel* const model =
       BookmarkModelFactory::GetForBrowserContext(profile);
-  const std::vector<const bookmarks::BookmarkNode*> nodes =
-      bookmarks::GetMostRecentlyModifiedUserFolders(model, 1);
-  return nodes[0]->GetTitle();
+
+  if (bookmarks::IsBookmarkedByUser(model, url)) {
+    const bookmarks::BookmarkNode* existing_node =
+        model->GetMostRecentlyAddedUserNodeForURL(url);
+    return existing_node->parent()->GetTitle();
+  }
+  const bookmarks::BookmarkNode* node = model->other_node();
+  return node->GetTitle();
 }
 
 std::unique_ptr<views::StyledLabel> CreateBodyLabel(std::u16string& body_text) {
@@ -59,7 +64,7 @@
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
 
-  auto folder_name = GetMostRecentlyModifiedUserBookmarkFolderName(profile_);
+  auto folder_name = GetBookmarkParentNameOrDefault(profile_, url);
 
   if (type == PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE) {
     SetTitle(l10n_util::GetStringUTF16(
diff --git a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc
index 610f2d5..471a84a 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/views/bookmarks/bookmark_editor_view.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/views/chrome_test_widget.h"
+#include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/strings/grit/components_strings.h"
@@ -68,15 +69,6 @@
     return factories;
   }
 
-  void SetUpDependencies() {
-    bookmarks::BookmarkModel* bookmark_model =
-        BookmarkModelFactory::GetForBrowserContext(profile());
-    bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
-
-    bookmarks::AddIfNotBookmarked(bookmark_model, GURL(kTestURL),
-                                  kTestBookmarkTitle);
-  }
-
   void CreateBubbleViewAndShow(PriceTrackingBubbleDialogView::Type type) {
     SkBitmap bitmap;
     bitmap.allocN32Pixels(1, 1);
@@ -101,14 +93,14 @@
     return bubble_coordinator_.get();
   }
 
-  const std::u16string& GetMostRecentlyModifiedUserBookmarkFolderName() {
-    bookmarks::BookmarkModel* const model =
-        BookmarkModelFactory::GetForBrowserContext(profile());
-    std::vector<const bookmarks::BookmarkNode*> nodes =
-        bookmarks::GetMostRecentlyModifiedUserFolders(model, 1);
-    return nodes[0]->GetTitle();
+ protected:
+  virtual void SetUpDependencies() {
+    bookmark_model_ = BookmarkModelFactory::GetForBrowserContext(profile());
+    bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_);
   }
 
+  bookmarks::BookmarkModel* bookmark_model_;
+
  private:
   views::UniqueWidgetPtr anchor_widget_;
   base::MockCallback<PriceTrackingBubbleDialogView::OnTrackPriceCallback>
@@ -117,7 +109,57 @@
   base::MockCallback<base::OnceClosure> on_dialog_closing_callback_;
 };
 
-TEST_F(PriceTrackingBubbleDialogViewUnitTest, FUEBubble) {
+class PriceTrackingBubbleDialogViewLayoutUnitTest
+    : public PriceTrackingBubbleDialogViewUnitTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  bool BookmarkWasCreated() { return GetParam(); }
+
+  static std::string DescribeParams(
+      const ::testing::TestParamInfo<ParamType>& info) {
+    if (info.param) {
+      return "TrackBookmarkedPage";
+    } else {
+      return "TrackNonBookmarkedPage";
+    }
+  }
+
+  const std::u16string& GetFolderName() {
+    if (BookmarkWasCreated()) {
+      return bookmark_folder_name_;
+    } else {
+      return GetDefaultFolderName();
+    }
+  }
+
+ protected:
+  void SetUpDependencies() override {
+    PriceTrackingBubbleDialogViewUnitTest::SetUpDependencies();
+
+    EXPECT_FALSE(
+        bookmarks::IsBookmarkedByUser(bookmark_model_, GURL(kTestURL)));
+
+    if (BookmarkWasCreated()) {
+      auto* node = bookmarks::AddIfNotBookmarked(
+          bookmark_model_, GURL(kTestURL), kTestBookmarkTitle);
+      EXPECT_TRUE(
+          bookmarks::IsBookmarkedByUser(bookmark_model_, GURL(kTestURL)));
+      bookmark_folder_name_ = node->parent()->GetTitle();
+    }
+  }
+
+ private:
+  const std::u16string& GetDefaultFolderName() {
+    bookmarks::BookmarkModel* const model =
+        BookmarkModelFactory::GetForBrowserContext(profile());
+    const bookmarks::BookmarkNode* node = model->other_node();
+    return node->GetTitle();
+  }
+
+  std::u16string bookmark_folder_name_;
+};
+
+TEST_P(PriceTrackingBubbleDialogViewLayoutUnitTest, FUEBubble) {
   CreateBubbleViewAndShow(
       PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE);
 
@@ -132,7 +174,7 @@
   EXPECT_EQ(bubble->GetBodyLabelForTesting()->GetText(),
             l10n_util::GetStringFUTF16(
                 IDS_OMNIBOX_TRACK_PRICE_DIALOG_DESCRIPTION_FIRST_RUN,
-                GetMostRecentlyModifiedUserBookmarkFolderName()));
+                GetFolderName()));
 
   EXPECT_EQ(
       bubble->GetDialogButtonLabel(ui::DIALOG_BUTTON_OK),
@@ -142,7 +184,7 @@
       l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACK_PRICE_DIALOG_CANCEL_BUTTON));
 }
 
-TEST_F(PriceTrackingBubbleDialogViewUnitTest, NormalBubble) {
+TEST_P(PriceTrackingBubbleDialogViewLayoutUnitTest, NormalBubble) {
   CreateBubbleViewAndShow(PriceTrackingBubbleDialogView::Type::TYPE_NORMAL);
 
   auto* bubble = BubbleCoordinator()->GetBubble();
@@ -152,10 +194,10 @@
             l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACKING_PRICE_DIALOG_TITLE));
 
   EXPECT_TRUE(bubble->GetBodyLabelForTesting());
-  EXPECT_EQ(bubble->GetBodyLabelForTesting()->GetText(),
-            l10n_util::GetStringFUTF16(
-                IDS_OMNIBOX_TRACKING_PRICE_DIALOG_DESCRIPTION,
-                GetMostRecentlyModifiedUserBookmarkFolderName()));
+  EXPECT_EQ(
+      bubble->GetBodyLabelForTesting()->GetText(),
+      l10n_util::GetStringFUTF16(IDS_OMNIBOX_TRACKING_PRICE_DIALOG_DESCRIPTION,
+                                 GetFolderName()));
   EXPECT_TRUE(bubble->GetBodyLabelForTesting()->GetFirstLinkForTesting());
 
   EXPECT_EQ(bubble->GetDialogButtonLabel(ui::DIALOG_BUTTON_OK),
@@ -166,7 +208,23 @@
                 IDS_OMNIBOX_TRACKING_PRICE_DIALOG_UNTRACK_BUTTON));
 }
 
-TEST_F(PriceTrackingBubbleDialogViewUnitTest, AcceptFUEBubble) {
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    PriceTrackingBubbleDialogViewLayoutUnitTest,
+    ::testing::Values(true, false),
+    &PriceTrackingBubbleDialogViewLayoutUnitTest::DescribeParams);
+
+class PriceTrackingBubbleDialogViewActionUnitTest
+    : public PriceTrackingBubbleDialogViewUnitTest {
+ protected:
+  void SetUpDependencies() override {
+    PriceTrackingBubbleDialogViewUnitTest::SetUpDependencies();
+    bookmarks::AddIfNotBookmarked(bookmark_model_, GURL(kTestURL),
+                                  kTestBookmarkTitle);
+  }
+};
+
+TEST_F(PriceTrackingBubbleDialogViewActionUnitTest, AcceptFUEBubble) {
   CreateBubbleViewAndShow(
       PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE);
 
@@ -177,7 +235,7 @@
   bubble->Accept();
 }
 
-TEST_F(PriceTrackingBubbleDialogViewUnitTest, CancelNormalBubble) {
+TEST_F(PriceTrackingBubbleDialogViewActionUnitTest, CancelNormalBubble) {
   CreateBubbleViewAndShow(PriceTrackingBubbleDialogView::Type::TYPE_NORMAL);
 
   auto* bubble = BubbleCoordinator()->GetBubble();
@@ -187,7 +245,8 @@
   bubble->Cancel();
 }
 
-TEST_F(PriceTrackingBubbleDialogViewUnitTest, ClickLinkInTheNormalBubble) {
+TEST_F(PriceTrackingBubbleDialogViewActionUnitTest,
+       ClickLinkInTheNormalBubble) {
   CreateBubbleViewAndShow(PriceTrackingBubbleDialogView::Type::TYPE_NORMAL);
 
   auto* bubble = BubbleCoordinator()->GetBubble();
diff --git a/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc b/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc
index 1d14c729..aaf3a3d3 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc
@@ -38,6 +38,25 @@
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/views/view_class_properties.h"
 
+namespace {
+
+void AddIfNotBookmarkedToTheDefaultFolder(bookmarks::BookmarkModel* model,
+                                          content::WebContents* web_contents) {
+  GURL url;
+  std::u16string title;
+
+  if (chrome::GetURLAndTitleToBookmark(web_contents, &url, &title)) {
+    if (bookmarks::IsBookmarkedByUser(model, url)) {
+      return;
+    }
+
+    const bookmarks::BookmarkNode* other_node = model->other_node();
+    model->AddNewURL(other_node, other_node->children().size(), title, url);
+  }
+}
+
+}  // namespace
+
 PriceTrackingIconView::PriceTrackingIconView(
     IconLabelBubbleView::Delegate* parent_delegate,
     Delegate* delegate,
@@ -195,11 +214,7 @@
   bool is_new_bookmark = existing_node == nullptr;
 
   if (enable) {
-    GURL url;
-    std::u16string title;
-    if (chrome::GetURLAndTitleToBookmark(GetWebContents(), &url, &title)) {
-      bookmarks::AddIfNotBookmarked(model, url, title);
-    }
+    AddIfNotBookmarkedToTheDefaultFolder(model, GetWebContents());
     base::RecordAction(
         base::UserMetricsAction("Commerce.PriceTracking.OmniboxChip.Tracked"));
     commerce::MaybeEnableEmailNotifications(profile_->GetPrefs());
diff --git a/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
index 9d4869b..0091344 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
@@ -76,9 +76,6 @@
         BookmarkModelFactory::GetForBrowserContext(browser()->profile());
     bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
 
-    bookmarks::AddIfNotBookmarked(bookmark_model, GURL(kTrackableUrl),
-                                  std::u16string());
-
     mock_shopping_service_ = static_cast<commerce::MockShoppingService*>(
         commerce::ShoppingServiceFactory::GetInstance()
             ->SetTestingFactoryAndUse(
@@ -146,6 +143,13 @@
     return matched_view ? views::AsViewClass<StarView>(matched_view) : nullptr;
   }
 
+  const std::u16string& GetDefaultFolderName() {
+    bookmarks::BookmarkModel* const model =
+        BookmarkModelFactory::GetForBrowserContext(browser()->profile());
+    const bookmarks::BookmarkNode* node = model->other_node();
+    return node->GetTitle();
+  }
+
  protected:
   base::UserActionTester user_action_tester_;
   raw_ptr<commerce::MockShoppingService, DanglingUntriaged>
@@ -252,6 +256,26 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PriceTrackingIconViewInteractiveTest,
+                       CreateBookmarkOnPressIfNotExist) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kShouldShowPriceTrackFUEBubble, false);
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  GURL url = GURL(kTrackableUrl);
+  bookmarks::BookmarkModel* bookmark_model =
+      BookmarkModelFactory::GetForBrowserContext(browser()->profile());
+  EXPECT_FALSE(bookmarks::IsBookmarkedByUser(bookmark_model, url));
+
+  ClickPriceTrackingIconView();
+  EXPECT_TRUE(bookmarks::IsBookmarkedByUser(bookmark_model, url));
+
+  const bookmarks::BookmarkNode* node =
+      bookmark_model->GetMostRecentlyAddedUserNodeForURL(url);
+  EXPECT_EQ(node->parent()->GetTitle(), GetDefaultFolderName());
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingIconViewInteractiveTest,
                        RecordOmniboxChipClicked) {
   EXPECT_EQ(user_action_tester_.GetActionCount(
                 "Commerce.PriceTracking.OmniboxChipClicked"),
diff --git a/chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc b/chrome/browser/ui/views/frame/browser_caption_button_container_win.cc
similarity index 84%
rename from chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc
rename to chrome/browser/ui/views/frame/browser_caption_button_container_win.cc
index 5776851..02c3ed9 100644
--- a/chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc
+++ b/chrome/browser/ui/views/frame/browser_caption_button_container_win.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/frame/glass_browser_caption_button_container.h"
+#include "chrome/browser/ui/views/frame/browser_caption_button_container_win.h"
 
 #include <memory>
 
 #include "chrome/browser/ui/frame/window_frame_util.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
 #include "chrome/browser/ui/views/frame/windows_caption_button.h"
 #include "chrome/browser/ui/views/frame/windows_tab_search_caption_button.h"
 #include "chrome/grit/generated_resources.h"
@@ -22,7 +22,7 @@
 
 std::unique_ptr<WindowsCaptionButton> CreateCaptionButton(
     views::Button::PressedCallback callback,
-    GlassBrowserFrameView* frame_view,
+    BrowserFrameViewWin* frame_view,
     ViewID button_type,
     int accessible_name_resource_id) {
   return std::make_unique<WindowsCaptionButton>(
@@ -37,8 +37,8 @@
 
 }  // anonymous namespace
 
-GlassBrowserCaptionButtonContainer::GlassBrowserCaptionButtonContainer(
-    GlassBrowserFrameView* frame_view)
+BrowserCaptionButtonContainer::BrowserCaptionButtonContainer(
+    BrowserFrameViewWin* frame_view)
     : frame_view_(frame_view),
       minimize_button_(AddChildView(CreateCaptionButton(
           base::BindRepeating(&BrowserFrame::Minimize,
@@ -87,18 +87,20 @@
                                    /* adjust_width_for_height */ false,
                                    views::MinimumFlexSizeRule::kScaleToZero));
 
-  if (frame_view_->browser_view()->AppUsesWindowControlsOverlay())
+  if (frame_view_->browser_view()->AppUsesWindowControlsOverlay()) {
     UpdateButtonToolTipsForWindowControlsOverlay();
+  }
 }
 
-GlassBrowserCaptionButtonContainer::~GlassBrowserCaptionButtonContainer() {}
+BrowserCaptionButtonContainer::~BrowserCaptionButtonContainer() = default;
 
-int GlassBrowserCaptionButtonContainer::NonClientHitTest(
+int BrowserCaptionButtonContainer::NonClientHitTest(
     const gfx::Point& point) const {
   DCHECK(HitTestPoint(point))
       << "should only be called with a point inside this view's bounds";
-  if (tab_search_button_ && HitTestCaptionButton(tab_search_button_, point))
+  if (tab_search_button_ && HitTestCaptionButton(tab_search_button_, point)) {
     return HTCLIENT;
+  }
   // BrowserView covers the frame view when Window Controls Overlay is enabled.
   // The native window that encompasses Web Contents gets the mouse events meant
   // for the caption buttons, so returning HTClient allows these buttons to be
@@ -107,21 +109,25 @@
       (HitTestCaptionButton(minimize_button_, point) ||
        HitTestCaptionButton(maximize_button_, point) ||
        HitTestCaptionButton(restore_button_, point) ||
-       HitTestCaptionButton(close_button_, point)))
+       HitTestCaptionButton(close_button_, point))) {
     return HTCLIENT;
-  if (HitTestCaptionButton(minimize_button_, point))
+  }
+  if (HitTestCaptionButton(minimize_button_, point)) {
     return HTMINBUTTON;
-  if (HitTestCaptionButton(maximize_button_, point))
+  }
+  if (HitTestCaptionButton(maximize_button_, point)) {
     return HTMAXBUTTON;
-  if (HitTestCaptionButton(restore_button_, point))
+  }
+  if (HitTestCaptionButton(restore_button_, point)) {
     return HTMAXBUTTON;
-  if (HitTestCaptionButton(close_button_, point))
+  }
+  if (HitTestCaptionButton(close_button_, point)) {
     return HTCLOSE;
+  }
   return HTCAPTION;
 }
 
-void GlassBrowserCaptionButtonContainer::
-    OnWindowControlsOverlayEnabledChanged() {
+void BrowserCaptionButtonContainer::OnWindowControlsOverlayEnabledChanged() {
   if (frame_view_->browser_view()->IsWindowControlsOverlayEnabled()) {
     SetBackground(
         views::CreateSolidBackground(frame_view_->GetTitlebarColor()));
@@ -136,13 +142,12 @@
   UpdateButtonToolTipsForWindowControlsOverlay();
 }
 
-TabSearchBubbleHost*
-GlassBrowserCaptionButtonContainer::GetTabSearchBubbleHost() {
+TabSearchBubbleHost* BrowserCaptionButtonContainer::GetTabSearchBubbleHost() {
   return tab_search_button_ ? tab_search_button_->tab_search_bubble_host()
                             : nullptr;
 }
 
-void GlassBrowserCaptionButtonContainer::OnThemeChanged() {
+void BrowserCaptionButtonContainer::OnThemeChanged() {
   if (frame_view_->browser_view()->IsWindowControlsOverlayEnabled()) {
     SetBackground(
         views::CreateSolidBackground(frame_view_->GetTitlebarColor()));
@@ -150,9 +155,10 @@
   views::View::OnThemeChanged();
 }
 
-void GlassBrowserCaptionButtonContainer::ResetWindowControls() {
-  if (tab_search_button_)
+void BrowserCaptionButtonContainer::ResetWindowControls() {
+  if (tab_search_button_) {
     tab_search_button_->SetState(views::Button::STATE_NORMAL);
+  }
   minimize_button_->SetState(views::Button::STATE_NORMAL);
   maximize_button_->SetState(views::Button::STATE_NORMAL);
   restore_button_->SetState(views::Button::STATE_NORMAL);
@@ -160,7 +166,7 @@
   InvalidateLayout();
 }
 
-void GlassBrowserCaptionButtonContainer::AddedToWidget() {
+void BrowserCaptionButtonContainer::AddedToWidget() {
   views::Widget* const widget = GetWidget();
 
   DCHECK(!widget_observation_.IsObserving());
@@ -177,18 +183,18 @@
   }
 }
 
-void GlassBrowserCaptionButtonContainer::RemovedFromWidget() {
+void BrowserCaptionButtonContainer::RemovedFromWidget() {
   DCHECK(widget_observation_.IsObserving());
   widget_observation_.Reset();
 }
 
-void GlassBrowserCaptionButtonContainer::OnWidgetBoundsChanged(
+void BrowserCaptionButtonContainer::OnWidgetBoundsChanged(
     views::Widget* widget,
     const gfx::Rect& new_bounds) {
   UpdateButtons();
 }
 
-void GlassBrowserCaptionButtonContainer::UpdateButtons() {
+void BrowserCaptionButtonContainer::UpdateButtons() {
   minimize_button_->SetVisible(frame_view_->browser_view()->CanMinimize());
 
   const bool is_maximized = frame_view_->IsMaximized();
@@ -205,7 +211,7 @@
   InvalidateLayout();
 }
 
-void GlassBrowserCaptionButtonContainer::
+void BrowserCaptionButtonContainer::
     UpdateButtonToolTipsForWindowControlsOverlay() {
   if (frame_view_->browser_view()->IsWindowControlsOverlayEnabled()) {
     minimize_button_->SetTooltipText(minimize_button_->GetAccessibleName());
@@ -220,5 +226,5 @@
   }
 }
 
-BEGIN_METADATA(GlassBrowserCaptionButtonContainer, views::View)
+BEGIN_METADATA(BrowserCaptionButtonContainer, views::View)
 END_METADATA
diff --git a/chrome/browser/ui/views/frame/glass_browser_caption_button_container.h b/chrome/browser/ui/views/frame/browser_caption_button_container_win.h
similarity index 75%
rename from chrome/browser/ui/views/frame/glass_browser_caption_button_container.h
rename to chrome/browser/ui/views/frame/browser_caption_button_container_win.h
index 0a89e37..6a51710e 100644
--- a/chrome/browser/ui/views/frame/glass_browser_caption_button_container.h
+++ b/chrome/browser/ui/views/frame/browser_caption_button_container_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_GLASS_BROWSER_CAPTION_BUTTON_CONTAINER_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_GLASS_BROWSER_CAPTION_BUTTON_CONTAINER_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_CAPTION_BUTTON_CONTAINER_WIN_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_CAPTION_BUTTON_CONTAINER_WIN_H_
 
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
@@ -14,7 +14,7 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
-class GlassBrowserFrameView;
+class BrowserFrameViewWin;
 class TabSearchBubbleHost;
 class WindowsCaptionButton;
 class WindowsTabSearchCaptionButton;
@@ -22,13 +22,12 @@
 // Provides a container for Windows caption buttons that can be moved between
 // frame and browser window as needed. When extended horizontally, becomes a
 // grab bar for moving the window.
-class GlassBrowserCaptionButtonContainer : public views::View,
-                                           public views::WidgetObserver {
+class BrowserCaptionButtonContainer : public views::View,
+                                      public views::WidgetObserver {
  public:
-  METADATA_HEADER(GlassBrowserCaptionButtonContainer);
-  explicit GlassBrowserCaptionButtonContainer(
-      GlassBrowserFrameView* frame_view);
-  ~GlassBrowserCaptionButtonContainer() override;
+  METADATA_HEADER(BrowserCaptionButtonContainer);
+  explicit BrowserCaptionButtonContainer(BrowserFrameViewWin* frame_view);
+  ~BrowserCaptionButtonContainer() override;
 
   // Tests to see if the specified |point| (which is expressed in this view's
   // coordinates and which must be within this view's bounds) is within one of
@@ -43,7 +42,7 @@
   TabSearchBubbleHost* GetTabSearchBubbleHost();
 
  private:
-  friend class GlassBrowserFrameView;
+  friend class BrowserFrameViewWin;
 
   // views::View:
   void AddedToWidget() override;
@@ -67,7 +66,7 @@
   // hwnd which prevent tooltips being shown for the caption buttons.
   void UpdateButtonToolTipsForWindowControlsOverlay();
 
-  const raw_ptr<GlassBrowserFrameView> frame_view_;
+  const raw_ptr<BrowserFrameViewWin> frame_view_;
   raw_ptr<WindowsTabSearchCaptionButton> tab_search_button_ = nullptr;
   const raw_ptr<WindowsCaptionButton> minimize_button_;
   const raw_ptr<WindowsCaptionButton> maximize_button_;
@@ -78,9 +77,9 @@
       widget_observation_{this};
 
   base::CallbackListSubscription subscription_ =
-      ui::TouchUiController::Get()->RegisterCallback(base::BindRepeating(
-          &GlassBrowserCaptionButtonContainer::UpdateButtons,
-          base::Unretained(this)));
+      ui::TouchUiController::Get()->RegisterCallback(
+          base::BindRepeating(&BrowserCaptionButtonContainer::UpdateButtons,
+                              base::Unretained(this)));
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_GLASS_BROWSER_CAPTION_BUTTON_CONTAINER_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_CAPTION_BUTTON_CONTAINER_WIN_H_
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.cc
similarity index 65%
rename from chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
rename to chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.cc
index 560b981..4ed411c6 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.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 "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 
 #include <tuple>
 
@@ -15,8 +15,8 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
+#include "chrome/browser/ui/views/frame/browser_caption_button_container_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/glass_browser_caption_button_container.h"
 #include "chrome/browser/ui/views/frame/windows_caption_button.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_test_helper.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
@@ -37,31 +37,32 @@
 #include "ui/color/color_provider.h"
 #include "ui/views/view_utils.h"
 
-class GlassBrowserFrameViewTest : public InProcessBrowserTest {
+class BrowserFrameViewWinTest : public InProcessBrowserTest {
  public:
-  GlassBrowserFrameViewTest() = default;
-  GlassBrowserFrameViewTest(const GlassBrowserFrameViewTest&) = delete;
-  GlassBrowserFrameViewTest& operator=(const GlassBrowserFrameViewTest&) =
-      delete;
-  ~GlassBrowserFrameViewTest() override = default;
+  BrowserFrameViewWinTest() = default;
+  BrowserFrameViewWinTest(const BrowserFrameViewWinTest&) = delete;
+  BrowserFrameViewWinTest& operator=(const BrowserFrameViewWinTest&) = delete;
+  ~BrowserFrameViewWinTest() override = default;
 
  protected:
-  GlassBrowserFrameView* GetGlassBrowserFrameView() {
+  BrowserFrameViewWin* GetBrowserFrameViewWin() {
     auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
     views::NonClientFrameView* frame_view =
         browser_view->GetWidget()->non_client_view()->frame_view();
 
-    if (!views::IsViewClass<GlassBrowserFrameView>(frame_view))
+    if (!views::IsViewClass<BrowserFrameViewWin>(frame_view)) {
       return nullptr;
-    return static_cast<GlassBrowserFrameView*>(frame_view);
+    }
+    return static_cast<BrowserFrameViewWin*>(frame_view);
   }
 
   const WindowsCaptionButton* GetMaximizeButton() {
-    auto* glass_frame_view = GetGlassBrowserFrameView();
-    if (!glass_frame_view)
+    auto* frame_view = GetBrowserFrameViewWin();
+    if (!frame_view) {
       return nullptr;
+    }
     auto* caption_button_container =
-        glass_frame_view->caption_button_container_for_testing();
+        frame_view->caption_button_container_for_testing();
     return static_cast<const WindowsCaptionButton*>(
         caption_button_container->GetViewByID(VIEW_ID_MAXIMIZE_BUTTON));
   }
@@ -69,12 +70,13 @@
 
 // Test that in touch mode, the maximize button is enabled for a non-maximized
 // window.
-IN_PROC_BROWSER_TEST_F(GlassBrowserFrameViewTest,
+IN_PROC_BROWSER_TEST_F(BrowserFrameViewWinTest,
                        NonMaximizedTouchMaximizeButtonState) {
   ui::TouchUiController::TouchUiScoperForTesting touch_ui_scoper_{true};
   auto* maximize_button = GetMaximizeButton();
-  if (!maximize_button)
+  if (!maximize_button) {
     GTEST_SKIP();
+  }
 
   EXPECT_TRUE(maximize_button->GetVisible());
   EXPECT_TRUE(maximize_button->GetEnabled());
@@ -82,14 +84,15 @@
 
 // Test that in touch mode, the maximize button is disabled and not visible for
 // a maximized window.
-IN_PROC_BROWSER_TEST_F(GlassBrowserFrameViewTest,
+IN_PROC_BROWSER_TEST_F(BrowserFrameViewWinTest,
                        MaximizedTouchMaximizeButtonState) {
   ui::TouchUiController::TouchUiScoperForTesting touch_ui_scoper_{true};
-  auto* glass_frame_view = GetGlassBrowserFrameView();
-  if (!glass_frame_view)
+  auto* frame_view = GetBrowserFrameViewWin();
+  if (!frame_view) {
     GTEST_SKIP();
+  }
 
-  glass_frame_view->frame()->Maximize();
+  frame_view->frame()->Maximize();
 
   auto* maximize_button = GetMaximizeButton();
 
@@ -100,12 +103,13 @@
 
 // Test that in non touch mode, the maximize button is enabled for a
 // non-maximized window.
-IN_PROC_BROWSER_TEST_F(GlassBrowserFrameViewTest,
+IN_PROC_BROWSER_TEST_F(BrowserFrameViewWinTest,
                        NonTouchNonMaximizedMaximizeButtonState) {
   ui::TouchUiController::TouchUiScoperForTesting touch_ui_scoper_{false};
   auto* maximize_button = GetMaximizeButton();
-  if (!maximize_button)
+  if (!maximize_button) {
     GTEST_SKIP();
+  }
 
   EXPECT_TRUE(maximize_button->GetVisible());
   EXPECT_TRUE(maximize_button->GetEnabled());
@@ -113,28 +117,28 @@
 
 // Test that in non touch mode, the maximize button is enabled and not visible
 // for a maximized window.
-IN_PROC_BROWSER_TEST_F(GlassBrowserFrameViewTest,
+IN_PROC_BROWSER_TEST_F(BrowserFrameViewWinTest,
                        NonTouchMaximizedMaximizeButtonState) {
   ui::TouchUiController::TouchUiScoperForTesting touch_ui_scoper_{false};
-  auto* glass_frame_view = GetGlassBrowserFrameView();
-  if (!glass_frame_view)
+  auto* frame_view = GetBrowserFrameViewWin();
+  if (!frame_view) {
     GTEST_SKIP();
+  }
 
-  glass_frame_view->frame()->Maximize();
+  frame_view->frame()->Maximize();
 
   auto* maximize_button = GetMaximizeButton();
   EXPECT_FALSE(maximize_button->GetVisible());
   EXPECT_TRUE(maximize_button->GetEnabled());
 }
 
-class WebAppGlassBrowserFrameViewTest : public InProcessBrowserTest {
+class WebAppBrowserFrameViewWinTest : public InProcessBrowserTest {
  public:
-  WebAppGlassBrowserFrameViewTest() = default;
-  WebAppGlassBrowserFrameViewTest(const WebAppGlassBrowserFrameViewTest&) =
-      delete;
-  WebAppGlassBrowserFrameViewTest& operator=(
-      const WebAppGlassBrowserFrameViewTest&) = delete;
-  ~WebAppGlassBrowserFrameViewTest() override = default;
+  WebAppBrowserFrameViewWinTest() = default;
+  WebAppBrowserFrameViewWinTest(const WebAppBrowserFrameViewWinTest&) = delete;
+  WebAppBrowserFrameViewWinTest& operator=(
+      const WebAppBrowserFrameViewWinTest&) = delete;
+  ~WebAppBrowserFrameViewWinTest() override = default;
 
   GURL GetStartURL() { return GURL("https://test.org"); }
 
@@ -144,16 +148,13 @@
     WebAppToolbarButtonContainer::DisableAnimationForTesting();
   }
 
-  // Windows 7 does not use GlassBrowserFrameView when Aero glass is not
-  // enabled. Skip testing in this scenario.
-  // TODO(https://crbug.com/863278): Force Aero glass on Windows 7 for this
-  // test.
-  bool InstallAndLaunchWebApp() {
+  void InstallAndLaunchWebApp() {
     auto web_app_info = std::make_unique<WebAppInstallInfo>();
     web_app_info->start_url = GetStartURL();
     web_app_info->scope = GetStartURL().GetWithoutFilename();
-    if (theme_color_)
+    if (theme_color_) {
       web_app_info->theme_color = *theme_color_;
+    }
 
     web_app::AppId app_id = web_app::test::InstallWebApp(
         browser()->profile(), std::move(web_app_info));
@@ -166,112 +167,98 @@
     views::NonClientFrameView* frame_view =
         browser_view_->GetWidget()->non_client_view()->frame_view();
 
-    if (!views::IsViewClass<GlassBrowserFrameView>(frame_view))
-      return false;
-    glass_frame_view_ = static_cast<GlassBrowserFrameView*>(frame_view);
+    frame_view_ = static_cast<BrowserFrameViewWin*>(frame_view);
 
     web_app_frame_toolbar_ = browser_view_->web_app_frame_toolbar_for_testing();
     DCHECK(web_app_frame_toolbar_);
     DCHECK(web_app_frame_toolbar_->GetVisible());
-    return true;
   }
 
   absl::optional<SkColor> theme_color_ = SK_ColorBLUE;
   raw_ptr<Browser, DanglingUntriaged> app_browser_ = nullptr;
   raw_ptr<BrowserView, DanglingUntriaged> browser_view_ = nullptr;
-  raw_ptr<GlassBrowserFrameView, DanglingUntriaged> glass_frame_view_ = nullptr;
+  raw_ptr<BrowserFrameViewWin, DanglingUntriaged> frame_view_ = nullptr;
   raw_ptr<WebAppFrameToolbarView, DanglingUntriaged> web_app_frame_toolbar_ =
       nullptr;
 };
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewTest, ThemeColor) {
-  if (!InstallAndLaunchWebApp())
-    return;
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, ThemeColor) {
+  InstallAndLaunchWebApp();
 
-  EXPECT_EQ(glass_frame_view_->GetTitlebarColor(), *theme_color_);
+  EXPECT_EQ(frame_view_->GetTitlebarColor(), *theme_color_);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewTest, NoThemeColor) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, NoThemeColor) {
   theme_color_ = absl::nullopt;
-  if (!InstallAndLaunchWebApp())
-    return;
+  InstallAndLaunchWebApp();
 
   EXPECT_EQ(
-      glass_frame_view_->GetTitlebarColor(),
+      frame_view_->GetTitlebarColor(),
       browser()->window()->GetColorProvider()->GetColor(ui::kColorFrameActive));
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewTest, MaximizedLayout) {
-  if (!InstallAndLaunchWebApp())
-    return;
-
-  glass_frame_view_->frame()->Maximize();
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, MaximizedLayout) {
+  InstallAndLaunchWebApp();
+  frame_view_->frame()->Maximize();
   RunScheduledLayouts();
 
   views::View* const window_title =
-      glass_frame_view_->GetViewByID(VIEW_ID_WINDOW_TITLE);
+      frame_view_->GetViewByID(VIEW_ID_WINDOW_TITLE);
   DCHECK_GT(window_title->x(), 0);
   DCHECK_GE(web_app_frame_toolbar_->y(), 0);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewTest, RTLTopRightHitTest) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, RTLTopRightHitTest) {
   base::i18n::SetRTLForTesting(true);
-  if (!InstallAndLaunchWebApp())
-    return;
-
+  InstallAndLaunchWebApp();
   RunScheduledLayouts();
 
   // Avoid the top right resize corner.
   constexpr int kInset = 10;
-  EXPECT_EQ(glass_frame_view_->NonClientHitTest(
-                gfx::Point(glass_frame_view_->width() - kInset, kInset)),
+  EXPECT_EQ(frame_view_->NonClientHitTest(
+                gfx::Point(frame_view_->width() - kInset, kInset)),
             HTCAPTION);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewTest, Fullscreen) {
-  if (!InstallAndLaunchWebApp())
-    return;
-
-  glass_frame_view_->frame()->SetFullscreen(true);
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, Fullscreen) {
+  InstallAndLaunchWebApp();
+  frame_view_->frame()->SetFullscreen(true);
   browser_view_->GetWidget()->LayoutRootViewIfNecessary();
 
   // Verify that all children except the ClientView are hidden when the window
   // is fullscreened.
-  for (views::View* child : glass_frame_view_->children()) {
+  for (views::View* child : frame_view_->children()) {
     EXPECT_EQ(views::IsViewClass<views::ClientView>(child),
               child->GetVisible());
   }
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewTest, ContainerHeight) {
-  if (!InstallAndLaunchWebApp())
-    return;
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, ContainerHeight) {
+  InstallAndLaunchWebApp();
 
-  static_cast<views::View*>(glass_frame_view_)
+  static_cast<views::View*>(frame_view_)
       ->GetWidget()
       ->LayoutRootViewIfNecessary();
 
-  EXPECT_EQ(
-      web_app_frame_toolbar_->height(),
-      glass_frame_view_->caption_button_container_for_testing()->height());
+  EXPECT_EQ(web_app_frame_toolbar_->height(),
+            frame_view_->caption_button_container_for_testing()->height());
 
-  glass_frame_view_->frame()->Maximize();
+  frame_view_->frame()->Maximize();
 
-  EXPECT_EQ(
-      web_app_frame_toolbar_->height(),
-      glass_frame_view_->caption_button_container_for_testing()->height());
+  EXPECT_EQ(web_app_frame_toolbar_->height(),
+            frame_view_->caption_button_container_for_testing()->height());
 }
 
-class WebAppGlassBrowserFrameViewWindowControlsOverlayTest
+class WebAppBrowserFrameViewWinWindowControlsOverlayTest
     : public InProcessBrowserTest {
  public:
-  WebAppGlassBrowserFrameViewWindowControlsOverlayTest() = default;
-  WebAppGlassBrowserFrameViewWindowControlsOverlayTest(
-      const WebAppGlassBrowserFrameViewWindowControlsOverlayTest&) = delete;
-  WebAppGlassBrowserFrameViewWindowControlsOverlayTest& operator=(
-      const WebAppGlassBrowserFrameViewWindowControlsOverlayTest&) = delete;
+  WebAppBrowserFrameViewWinWindowControlsOverlayTest() = default;
+  WebAppBrowserFrameViewWinWindowControlsOverlayTest(
+      const WebAppBrowserFrameViewWinWindowControlsOverlayTest&) = delete;
+  WebAppBrowserFrameViewWinWindowControlsOverlayTest& operator=(
+      const WebAppBrowserFrameViewWinWindowControlsOverlayTest&) = delete;
 
-  ~WebAppGlassBrowserFrameViewWindowControlsOverlayTest() override = default;
+  ~WebAppBrowserFrameViewWinWindowControlsOverlayTest() override = default;
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -280,7 +267,7 @@
     InProcessBrowserTest::SetUp();
   }
 
-  bool InstallAndLaunchWebAppWithWindowControlsOverlay() {
+  void InstallAndLaunchWebAppWithWindowControlsOverlay() {
     GURL start_url = web_app_frame_toolbar_helper_
                          .LoadWindowControlsOverlayTestPageWithDataAndGetURL(
                              embedded_test_server(), &temp_dir_);
@@ -317,16 +304,12 @@
     views::NonClientFrameView* frame_view =
         browser_view_->GetWidget()->non_client_view()->frame_view();
 
-    if (!views::IsViewClass<GlassBrowserFrameView>(frame_view))
-      return false;
-
-    glass_frame_view_ = static_cast<GlassBrowserFrameView*>(frame_view);
+    frame_view_ = static_cast<BrowserFrameViewWin*>(frame_view);
     auto* web_app_frame_toolbar =
         browser_view_->web_app_frame_toolbar_for_testing();
 
     DCHECK(web_app_frame_toolbar);
     DCHECK(web_app_frame_toolbar->GetVisible());
-    return true;
   }
 
   void ToggleWindowControlsOverlayEnabledAndWait() {
@@ -340,54 +323,46 @@
   }
 
   raw_ptr<BrowserView, DanglingUntriaged> browser_view_ = nullptr;
-  raw_ptr<GlassBrowserFrameView, DanglingUntriaged> glass_frame_view_ = nullptr;
+  raw_ptr<BrowserFrameViewWin, DanglingUntriaged> frame_view_ = nullptr;
   WebAppFrameToolbarTestHelper web_app_frame_toolbar_helper_;
 
  private:
   base::ScopedTempDir temp_dir_;
 };
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewWindowControlsOverlayTest,
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinWindowControlsOverlayTest,
                        ContainerHeight) {
-  if (!InstallAndLaunchWebAppWithWindowControlsOverlay())
-    return;
-
+  InstallAndLaunchWebAppWithWindowControlsOverlay();
   ToggleWindowControlsOverlayEnabledAndWait();
 
-  EXPECT_EQ(
-      browser_view_->web_app_frame_toolbar_for_testing()->height(),
-      glass_frame_view_->caption_button_container_for_testing()->height());
+  EXPECT_EQ(browser_view_->web_app_frame_toolbar_for_testing()->height(),
+            frame_view_->caption_button_container_for_testing()->height());
 
-  glass_frame_view_->frame()->Maximize();
+  frame_view_->frame()->Maximize();
 
-  EXPECT_EQ(
-      browser_view_->web_app_frame_toolbar_for_testing()->height(),
-      glass_frame_view_->caption_button_container_for_testing()->height());
+  EXPECT_EQ(browser_view_->web_app_frame_toolbar_for_testing()->height(),
+            frame_view_->caption_button_container_for_testing()->height());
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewWindowControlsOverlayTest,
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinWindowControlsOverlayTest,
                        Fullscreen) {
-  if (!InstallAndLaunchWebAppWithWindowControlsOverlay())
-    return;
-
+  InstallAndLaunchWebAppWithWindowControlsOverlay();
   ToggleWindowControlsOverlayEnabledAndWait();
 
-  EXPECT_GT(glass_frame_view_->GetBoundsForClientView().y(), 0);
+  EXPECT_GT(frame_view_->GetBoundsForClientView().y(), 0);
 
-  glass_frame_view_->frame()->SetFullscreen(true);
+  frame_view_->frame()->SetFullscreen(true);
   browser_view_->GetWidget()->LayoutRootViewIfNecessary();
 
   // ClientView should be covering the entire screen.
-  EXPECT_EQ(glass_frame_view_->GetBoundsForClientView().y(), 0);
+  EXPECT_EQ(frame_view_->GetBoundsForClientView().y(), 0);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewWindowControlsOverlayTest,
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinWindowControlsOverlayTest,
                        CaptionButtonsTooltip) {
-  if (!InstallAndLaunchWebAppWithWindowControlsOverlay())
-    return;
-
+  InstallAndLaunchWebAppWithWindowControlsOverlay();
   auto* caption_button_container =
-      glass_frame_view_->caption_button_container_for_testing();
+      frame_view_->caption_button_container_for_testing();
   auto* minimize_button = static_cast<const WindowsCaptionButton*>(
       caption_button_container->GetViewByID(VIEW_ID_MINIMIZE_BUTTON));
   auto* maximize_button = static_cast<const WindowsCaptionButton*>(
@@ -423,39 +398,35 @@
   EXPECT_EQ(close_button->GetTooltipText(), u"");
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewWindowControlsOverlayTest,
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinWindowControlsOverlayTest,
                        CaptionButtonHitTest) {
-  if (!InstallAndLaunchWebAppWithWindowControlsOverlay())
-    return;
-
-  glass_frame_view_->GetWidget()->LayoutRootViewIfNecessary();
+  InstallAndLaunchWebAppWithWindowControlsOverlay();
+  frame_view_->GetWidget()->LayoutRootViewIfNecessary();
 
   // Avoid the top right resize corner.
   constexpr int kInset = 10;
-  const gfx::Point kPoint(glass_frame_view_->width() - kInset, kInset);
+  const gfx::Point kPoint(frame_view_->width() - kInset, kInset);
 
-  EXPECT_EQ(glass_frame_view_->NonClientHitTest(kPoint), HTCLOSE);
+  EXPECT_EQ(frame_view_->NonClientHitTest(kPoint), HTCLOSE);
 
   ToggleWindowControlsOverlayEnabledAndWait();
 
   // Verify the component updates on toggle.
-  EXPECT_EQ(glass_frame_view_->NonClientHitTest(kPoint), HTCLIENT);
+  EXPECT_EQ(frame_view_->NonClientHitTest(kPoint), HTCLIENT);
 
   ToggleWindowControlsOverlayEnabledAndWait();
 
   // Verify the component clears when the feature is turned off.
-  EXPECT_EQ(glass_frame_view_->NonClientHitTest(kPoint), HTCLOSE);
+  EXPECT_EQ(frame_view_->NonClientHitTest(kPoint), HTCLOSE);
 }
 
 // Regression test for https://crbug.com/1286896.
-IN_PROC_BROWSER_TEST_F(WebAppGlassBrowserFrameViewWindowControlsOverlayTest,
+IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinWindowControlsOverlayTest,
                        TitlebarLayoutAfterUpdateWindowTitle) {
-  if (!InstallAndLaunchWebAppWithWindowControlsOverlay())
-    return;
-
+  InstallAndLaunchWebAppWithWindowControlsOverlay();
   ToggleWindowControlsOverlayEnabledAndWait();
-  glass_frame_view_->GetWidget()->LayoutRootViewIfNecessary();
-  glass_frame_view_->UpdateWindowTitle();
+  frame_view_->GetWidget()->LayoutRootViewIfNecessary();
+  frame_view_->UpdateWindowTitle();
 
   WebAppFrameToolbarView* web_app_frame_toolbar =
       browser_view_->web_app_frame_toolbar_for_testing();
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/browser_frame_view_win.cc
similarity index 86%
rename from chrome/browser/ui/views/frame/glass_browser_frame_view.cc
rename to chrome/browser/ui/views/frame/browser_frame_view_win.cc
index 2f8981f..c2c1f722 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_win.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 "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 
 #include <dwmapi.h>
 
@@ -17,8 +17,8 @@
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/frame/browser_caption_button_container_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/glass_browser_caption_button_container.h"
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/frame/webui_tab_strip_container_view.h"
 #include "chrome/browser/ui/views/tabs/new_tab_button.h"
@@ -48,8 +48,8 @@
 #include "ui/views/win/hwnd_util.h"
 #include "ui/views/window/client_view.h"
 
-HICON GlassBrowserFrameView::throbber_icons_[
-    GlassBrowserFrameView::kThrobberIconCount];
+HICON BrowserFrameViewWin::throbber_icons_
+    [BrowserFrameViewWin::kThrobberIconCount];
 
 namespace {
 
@@ -74,10 +74,10 @@
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
-// GlassBrowserFrameView, public:
+// BrowserFrameViewWin, public:
 
-GlassBrowserFrameView::GlassBrowserFrameView(BrowserFrame* frame,
-                                             BrowserView* browser_view)
+BrowserFrameViewWin::BrowserFrameViewWin(BrowserFrame* frame,
+                                         BrowserView* browser_view)
     : BrowserNonClientFrameView(frame, browser_view) {
   // We initialize all fields despite some of them being unused in some modes,
   // since it's possible for modes to flip dynamically (e.g. if the user enables
@@ -126,34 +126,35 @@
   }
 
   caption_button_container_ =
-      AddChildView(std::make_unique<GlassBrowserCaptionButtonContainer>(this));
+      AddChildView(std::make_unique<BrowserCaptionButtonContainer>(this));
 }
 
-GlassBrowserFrameView::~GlassBrowserFrameView() = default;
+BrowserFrameViewWin::~BrowserFrameViewWin() = default;
 
 ///////////////////////////////////////////////////////////////////////////////
-// GlassBrowserFrameView, BrowserNonClientFrameView implementation:
+// BrowserFrameViewWin, BrowserNonClientFrameView implementation:
 
-bool GlassBrowserFrameView::CaptionButtonsOnLeadingEdge() const {
+bool BrowserFrameViewWin::CaptionButtonsOnLeadingEdge() const {
   // Because we don't set WS_EX_LAYOUTRTL (which would conflict with Chrome's
   // own RTL layout logic), Windows always draws the caption buttons on the
   // right, even when we want to be RTL. See crbug.com/560619.
   return !ShouldCustomDrawSystemTitlebar() && base::i18n::IsRTL();
 }
 
-gfx::Rect GlassBrowserFrameView::GetBoundsForTabStripRegion(
+gfx::Rect BrowserFrameViewWin::GetBoundsForTabStripRegion(
     const gfx::Size& tabstrip_minimum_size) const {
   const int x = CaptionButtonsOnLeadingEdge()
                     ? (width() - frame()->GetMinimizeButtonOffset())
                     : 0;
   int end_x = width();
-  if (!CaptionButtonsOnLeadingEdge())
+  if (!CaptionButtonsOnLeadingEdge()) {
     end_x = std::min(MinimizeButtonX(), end_x);
+  }
   return gfx::Rect(x, TopAreaHeight(false), std::max(0, end_x - x),
                    tabstrip_minimum_size.height());
 }
 
-gfx::Rect GlassBrowserFrameView::GetBoundsForWebAppFrameToolbar(
+gfx::Rect BrowserFrameViewWin::GetBoundsForWebAppFrameToolbar(
     const gfx::Size& toolbar_preferred_size) const {
   int x = display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
   if (IsMaximized()) {
@@ -167,7 +168,7 @@
                    caption_button_container_->size().height());
 }
 
-void GlassBrowserFrameView::LayoutWebAppWindowTitle(
+void BrowserFrameViewWin::LayoutWebAppWindowTitle(
     const gfx::Rect& available_space,
     views::Label& window_title_label) const {
   gfx::Rect bounds = available_space;
@@ -184,17 +185,18 @@
   window_title_label.SetBoundsRect(bounds);
 }
 
-int GlassBrowserFrameView::GetTopInset(bool restored) const {
-  if (browser_view()->GetTabStripVisible() || IsWebUITabStrip())
+int BrowserFrameViewWin::GetTopInset(bool restored) const {
+  if (browser_view()->GetTabStripVisible() || IsWebUITabStrip()) {
     return TopAreaHeight(restored);
+  }
   return ShouldCustomDrawSystemTitlebar() ? TitlebarHeight(restored) : 0;
 }
 
-int GlassBrowserFrameView::GetThemeBackgroundXInset() const {
+int BrowserFrameViewWin::GetThemeBackgroundXInset() const {
   return 0;
 }
 
-bool GlassBrowserFrameView::HasVisibleBackgroundTabShapes(
+bool BrowserFrameViewWin::HasVisibleBackgroundTabShapes(
     BrowserFrameActiveState active_state) const {
   DCHECK(GetWidget());
 
@@ -204,20 +206,21 @@
   // colors).
   // TODO(pkasting): https://crbug.com/831769  Change the architecture of the
   // high contrast support to respect system colors, then remove this.
-  if (GetNativeTheme()->UserHasContrastPreference())
+  if (GetNativeTheme()->UserHasContrastPreference()) {
     return true;
+  }
 
   return BrowserNonClientFrameView::HasVisibleBackgroundTabShapes(active_state);
 }
 
-SkColor GlassBrowserFrameView::GetCaptionColor(
+SkColor BrowserFrameViewWin::GetCaptionColor(
     BrowserFrameActiveState active_state) const {
   return GetColorProvider()->GetColor(ShouldPaintAsActive(active_state)
                                           ? kColorCaptionForegroundActive
                                           : kColorCaptionForegroundInactive);
 }
 
-void GlassBrowserFrameView::UpdateThrobber(bool running) {
+void BrowserFrameViewWin::UpdateThrobber(bool running) {
   if (ShouldShowWindowIcon(TitlebarType::kCustom)) {
     window_icon_->Update();
   } else if (ShouldShowWindowIcon(TitlebarType::kSystem)) {
@@ -233,40 +236,41 @@
   }
 }
 
-gfx::Size GlassBrowserFrameView::GetMinimumSize() const {
+gfx::Size BrowserFrameViewWin::GetMinimumSize() const {
   gfx::Size min_size(browser_view()->GetMinimumSize());
   min_size.Enlarge(0, GetTopInset(false));
 
   return min_size;
 }
 
-void GlassBrowserFrameView::WindowControlsOverlayEnabledChanged() {
+void BrowserFrameViewWin::WindowControlsOverlayEnabledChanged() {
   caption_button_container_->OnWindowControlsOverlayEnabledChanged();
 }
 
-TabSearchBubbleHost* GlassBrowserFrameView::GetTabSearchBubbleHost() {
+TabSearchBubbleHost* BrowserFrameViewWin::GetTabSearchBubbleHost() {
   return caption_button_container_->GetTabSearchBubbleHost();
 }
 
-void GlassBrowserFrameView::PaintAsActiveChanged() {
+void BrowserFrameViewWin::PaintAsActiveChanged() {
   BrowserNonClientFrameView::PaintAsActiveChanged();
 
   // When window controls overlay is enabled, the caption button container is
   // painted to a layer and is not repainted by
   // BrowserNonClientFrameView::PaintAsActiveChanged. Schedule a re-paint here
   // to update the caption button colors.
-  if (caption_button_container_ && caption_button_container_->layer())
+  if (caption_button_container_ && caption_button_container_->layer()) {
     caption_button_container_->SchedulePaint();
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// GlassBrowserFrameView, views::NonClientFrameView implementation:
+// BrowserFrameViewWin, views::NonClientFrameView implementation:
 
-gfx::Rect GlassBrowserFrameView::GetBoundsForClientView() const {
+gfx::Rect BrowserFrameViewWin::GetBoundsForClientView() const {
   return client_view_bounds_;
 }
 
-gfx::Rect GlassBrowserFrameView::GetWindowBoundsForClientBounds(
+gfx::Rect BrowserFrameViewWin::GetWindowBoundsForClientBounds(
     const gfx::Rect& client_bounds) const {
   HWND hwnd = views::HWNDForWidget(frame());
   if (!browser_view()->GetTabStripVisible() && hwnd) {
@@ -286,20 +290,23 @@
                    client_bounds.width(), client_bounds.height() + top_inset);
 }
 
-int GlassBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
+int BrowserFrameViewWin::NonClientHitTest(const gfx::Point& point) {
   int super_component = BrowserNonClientFrameView::NonClientHitTest(point);
-  if (super_component != HTNOWHERE)
+  if (super_component != HTNOWHERE) {
     return super_component;
+  }
 
   // For app windows and popups without a custom titlebar we haven't customized
   // the frame at all so Windows can figure it out.
-  if (!ShouldCustomDrawSystemTitlebar() && !browser_view()->GetIsNormalType())
+  if (!ShouldCustomDrawSystemTitlebar() && !browser_view()->GetIsNormalType()) {
     return HTNOWHERE;
+  }
 
   // If the point isn't within our bounds, then it's in the native portion of
   // the frame so again Windows can figure it out.
-  if (!bounds().Contains(point))
+  if (!bounds().Contains(point)) {
     return HTNOWHERE;
+  }
 
   int frame_component = frame()->client_view()->NonClientHitTest(point);
 
@@ -310,12 +317,14 @@
         0, display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSIZEFRAME),
         display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSMICON),
         display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSMICON));
-    if (sys_menu_region.Contains(point))
+    if (sys_menu_region.Contains(point)) {
       return HTSYSMENU;
+    }
   }
 
-  if (frame_component != HTNOWHERE)
+  if (frame_component != HTNOWHERE) {
     return frame_component;
+  }
 
   // Then see if the point is within any of the window controls.
   if (caption_button_container_) {
@@ -324,8 +333,9 @@
     if (caption_button_container_->HitTestPoint(local_point)) {
       const int hit_test_result =
           caption_button_container_->NonClientHitTest(local_point);
-      if (hit_test_result != HTNOWHERE)
+      if (hit_test_result != HTNOWHERE) {
         return hit_test_result;
+      }
     }
   }
 
@@ -377,75 +387,81 @@
   return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
 }
 
-void GlassBrowserFrameView::UpdateWindowIcon() {
-  if (window_icon_ && window_icon_->GetVisible())
+void BrowserFrameViewWin::UpdateWindowIcon() {
+  if (window_icon_ && window_icon_->GetVisible()) {
     window_icon_->SchedulePaint();
+  }
 }
 
-void GlassBrowserFrameView::UpdateWindowTitle() {
+void BrowserFrameViewWin::UpdateWindowTitle() {
   LayoutTitleBar();
-  if (window_title_ && window_title_->GetVisible())
+  if (window_title_ && window_title_->GetVisible()) {
     window_title_->SchedulePaint();
+  }
 }
 
-void GlassBrowserFrameView::ResetWindowControls() {
+void BrowserFrameViewWin::ResetWindowControls() {
   BrowserNonClientFrameView::ResetWindowControls();
-  if (caption_button_container_)
+  if (caption_button_container_) {
     caption_button_container_->ResetWindowControls();
+  }
 }
 
-bool GlassBrowserFrameView::ShouldTabIconViewAnimate() const {
-  if (!ShouldShowWindowIcon(TitlebarType::kCustom))
+bool BrowserFrameViewWin::ShouldTabIconViewAnimate() const {
+  if (!ShouldShowWindowIcon(TitlebarType::kCustom)) {
     return false;
+  }
   content::WebContents* current_tab = browser_view()->GetActiveWebContents();
   return current_tab && current_tab->IsLoading();
 }
 
-ui::ImageModel GlassBrowserFrameView::GetFaviconForTabIconView() {
+ui::ImageModel BrowserFrameViewWin::GetFaviconForTabIconView() {
   DCHECK(ShouldShowWindowIcon(TitlebarType::kCustom));
   return frame()->widget_delegate()->GetWindowIcon();
 }
 
-bool GlassBrowserFrameView::IsMaximized() const {
+bool BrowserFrameViewWin::IsMaximized() const {
   return frame()->IsMaximized();
 }
 
-bool GlassBrowserFrameView::IsWebUITabStrip() const {
+bool BrowserFrameViewWin::IsWebUITabStrip() const {
   return WebUITabStripContainerView::UseTouchableTabStrip(
       browser_view()->browser());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// GlassBrowserFrameView, views::View overrides:
+// BrowserFrameViewWin, views::View overrides:
 
-void GlassBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
-  TRACE_EVENT0("views.frame", "GlassBrowserFrameView::OnPaint");
-  if (ShouldCustomDrawSystemTitlebar())
+void BrowserFrameViewWin::OnPaint(gfx::Canvas* canvas) {
+  TRACE_EVENT0("views.frame", "BrowserFrameViewWin::OnPaint");
+  if (ShouldCustomDrawSystemTitlebar()) {
     PaintTitlebar(canvas);
+  }
 }
 
-void GlassBrowserFrameView::Layout() {
-  TRACE_EVENT0("views.frame", "GlassBrowserFrameView::Layout");
+void BrowserFrameViewWin::Layout() {
+  TRACE_EVENT0("views.frame", "BrowserFrameViewWin::Layout");
 
   LayoutCaptionButtons();
-  if (browser_view()->IsWindowControlsOverlayEnabled())
+  if (browser_view()->IsWindowControlsOverlayEnabled()) {
     LayoutWindowControlsOverlay();
-  else
+  } else {
     LayoutTitleBar();
+  }
   LayoutClientView();
   BrowserNonClientFrameView::Layout();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// GlassBrowserFrameView, private:
+// BrowserFrameViewWin, private:
 
-int GlassBrowserFrameView::FrameBorderThickness() const {
+int BrowserFrameViewWin::FrameBorderThickness() const {
   return (IsMaximized() || frame()->IsFullscreen())
              ? 0
              : display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
 }
 
-int GlassBrowserFrameView::FrameTopBorderThickness(bool restored) const {
+int BrowserFrameViewWin::FrameTopBorderThickness(bool restored) const {
   const bool is_fullscreen =
       (frame()->IsFullscreen() || IsMaximized()) && !restored;
   if (!is_fullscreen) {
@@ -454,8 +470,9 @@
     // extends beyond the screen edges. In that case, we must return the default
     // value.
     constexpr int kTopResizeFrameArea = 5;
-    if (browser_view()->GetTabStripVisible())
+    if (browser_view()->GetTabStripVisible()) {
       return kTopResizeFrameArea;
+    }
 
     // There is no top border in tablet mode when the window is "restored"
     // because it is still tiled into either the left or right pane of the
@@ -463,8 +480,9 @@
     // rendering bug in Windows may still cause the very top of the window to be
     // cut off intermittently, but that's an OS issue that affects all
     // applications, not specifically Chrome.
-    if (IsWebUITabStrip())
+    if (IsWebUITabStrip()) {
       return 0;
+    }
   }
 
   // Mouse and touch locations are floored but GetSystemMetricsInDIP is rounded,
@@ -475,7 +493,7 @@
       display::win::ScreenWin::GetScaleFactorForHWND(HWNDForView(this)));
 }
 
-int GlassBrowserFrameView::FrameTopBorderThicknessPx(bool restored) const {
+int BrowserFrameViewWin::FrameTopBorderThicknessPx(bool restored) const {
   // Distinct from FrameBorderThickness() because we can't inset the top
   // border, otherwise Windows will give us a standard titlebar.
   // For maximized windows this is not true, and the top border must be
@@ -484,8 +502,9 @@
   const bool needs_no_border =
       (ShouldCustomDrawSystemTitlebar() && frame()->IsMaximized()) ||
       frame()->IsFullscreen();
-  if (needs_no_border && !restored)
+  if (needs_no_border && !restored) {
     return 0;
+  }
 
   // Note that this method assumes an equal resize handle thickness on all
   // sides of the window.
@@ -494,9 +513,10 @@
       MonitorFromWindow(HWNDForView(this), MONITOR_DEFAULTTONEAREST));
 }
 
-int GlassBrowserFrameView::TopAreaHeight(bool restored) const {
-  if (frame()->IsFullscreen() && !restored)
+int BrowserFrameViewWin::TopAreaHeight(bool restored) const {
+  if (frame()->IsFullscreen() && !restored) {
     return 0;
+  }
 
   const bool maximized = IsMaximized() && !restored;
   int top = FrameTopBorderThickness(restored);
@@ -511,8 +531,9 @@
 
   // In maximized mode, we do not add any additional thickness to the grab
   // handle above the tabs; just return the frame thickness.
-  if (maximized)
+  if (maximized) {
     return top;
+  }
 
   // Besides the frame border, there's empty space atop the window in restored
   // mode, to use to drag the window around.
@@ -525,7 +546,7 @@
   return top + thickness;
 }
 
-int GlassBrowserFrameView::TitlebarMaximizedVisualHeight() const {
+int BrowserFrameViewWin::TitlebarMaximizedVisualHeight() const {
   int maximized_height =
       display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYCAPTION);
   // Adding 2 dip of vertical padding puts at least 1 dip of space on the top
@@ -547,9 +568,10 @@
   return maximized_height;
 }
 
-int GlassBrowserFrameView::TitlebarHeight(bool restored) const {
-  if (frame()->IsFullscreen() && !restored)
+int BrowserFrameViewWin::TitlebarHeight(bool restored) const {
+  if (frame()->IsFullscreen() && !restored) {
     return 0;
+  }
 
   // The titlebar's actual height is the same in restored and maximized, but
   // some of it is above the screen in maximized mode. See the comment in
@@ -560,17 +582,18 @@
          FrameTopBorderThickness(false);
 }
 
-int GlassBrowserFrameView::WindowTopY() const {
+int BrowserFrameViewWin::WindowTopY() const {
   // The window top is SM_CYSIZEFRAME pixels when maximized (see the comment in
   // FrameTopBorderThickness()) and floor(system dsf) pixels when restored.
   // Unfortunately we can't represent either of those at hidpi without using
   // non-integral dips, so we return the closest reasonable values instead.
-  if (IsMaximized())
+  if (IsMaximized()) {
     return FrameTopBorderThickness(false);
+  }
   return IsWebUITabStrip() ? FrameTopBorderThickness(true) : 1;
 }
 
-int GlassBrowserFrameView::MinimizeButtonX() const {
+int BrowserFrameViewWin::MinimizeButtonX() const {
   // When CaptionButtonsOnLeadingEdge() is true call
   // frame()->GetMinimizeButtonOffset() directly, because minimize_button_->x()
   // will give the wrong edge of the button.
@@ -584,32 +607,38 @@
              : frame()->GetMinimizeButtonOffset();
 }
 
-bool GlassBrowserFrameView::ShouldShowWindowIcon(TitlebarType type) const {
-  if (type == TitlebarType::kCustom && !ShouldCustomDrawSystemTitlebar())
+bool BrowserFrameViewWin::ShouldShowWindowIcon(TitlebarType type) const {
+  if (type == TitlebarType::kCustom && !ShouldCustomDrawSystemTitlebar()) {
     return false;
-  if (type == TitlebarType::kSystem && ShouldCustomDrawSystemTitlebar())
+  }
+  if (type == TitlebarType::kSystem && ShouldCustomDrawSystemTitlebar()) {
     return false;
-  if (frame()->IsFullscreen() || browser_view()->GetIsWebAppType())
+  }
+  if (frame()->IsFullscreen() || browser_view()->GetIsWebAppType()) {
     return false;
+  }
   return browser_view()->ShouldShowWindowIcon();
 }
 
-bool GlassBrowserFrameView::ShouldShowWindowTitle(TitlebarType type) const {
-  if (type == TitlebarType::kCustom && !ShouldCustomDrawSystemTitlebar())
+bool BrowserFrameViewWin::ShouldShowWindowTitle(TitlebarType type) const {
+  if (type == TitlebarType::kCustom && !ShouldCustomDrawSystemTitlebar()) {
     return false;
-  if (type == TitlebarType::kSystem && ShouldCustomDrawSystemTitlebar())
+  }
+  if (type == TitlebarType::kSystem && ShouldCustomDrawSystemTitlebar()) {
     return false;
-  if (frame()->IsFullscreen())
+  }
+  if (frame()->IsFullscreen()) {
     return false;
+  }
   return browser_view()->ShouldShowWindowTitle();
 }
 
-SkColor GlassBrowserFrameView::GetTitlebarColor() const {
+SkColor BrowserFrameViewWin::GetTitlebarColor() const {
   return GetFrameColor(BrowserFrameActiveState::kUseCurrent);
 }
 
-void GlassBrowserFrameView::PaintTitlebar(gfx::Canvas* canvas) const {
-  TRACE_EVENT0("views.frame", "GlassBrowserFrameView::PaintTitlebar");
+void BrowserFrameViewWin::PaintTitlebar(gfx::Canvas* canvas) const {
+  TRACE_EVENT0("views.frame", "BrowserFrameViewWin::PaintTitlebar");
 
   // This is the pixel-accurate version of WindowTopY(). Scaling the DIP values
   // here compounds precision error, which exposes unpainted client area. When
@@ -681,14 +710,16 @@
   }
 }
 
-void GlassBrowserFrameView::LayoutTitleBar() {
-  TRACE_EVENT0("views.frame", "GlassBrowserFrameView::LayoutTitleBar");
+void BrowserFrameViewWin::LayoutTitleBar() {
+  TRACE_EVENT0("views.frame", "BrowserFrameViewWin::LayoutTitleBar");
   const bool show_icon = ShouldShowWindowIcon(TitlebarType::kCustom);
   const bool show_title = ShouldShowWindowTitle(TitlebarType::kCustom);
-  if (window_icon_)
+  if (window_icon_) {
     window_icon_->SetVisible(show_icon);
-  if (window_title_)
+  }
+  if (window_title_) {
     window_title_->SetVisible(show_title);
+  }
   if (!show_icon && !show_title &&
       (!web_app_frame_toolbar() || frame()->IsFullscreen())) {
     // TODO(crbug.com/1132767): The "frame()->IsFullscreen()" term is required
@@ -707,8 +738,9 @@
   const int window_top = IsMaximized() ? WindowTopY() : 0;
   int next_leading_x =
       display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
-  if (IsMaximized())
+  if (IsMaximized()) {
     next_leading_x += kMaximizedLeftMargin;
+  }
   int next_trailing_x = MinimizeButtonX();
 
   const int y = window_top + (titlebar_visual_height - icon_size) / 2;
@@ -743,10 +775,11 @@
   }
 }
 
-void GlassBrowserFrameView::LayoutCaptionButtons() {
-  TRACE_EVENT0("views.frame", "GlassBrowserFrameView::LayoutCaptionButtons");
-  if (!caption_button_container_)
+void BrowserFrameViewWin::LayoutCaptionButtons() {
+  TRACE_EVENT0("views.frame", "BrowserFrameViewWin::LayoutCaptionButtons");
+  if (!caption_button_container_) {
     return;
+  }
 
   // Non-custom system titlebar already contains caption buttons.
   if (!ShouldCustomDrawSystemTitlebar()) {
@@ -778,7 +811,7 @@
                                        height);
 }
 
-void GlassBrowserFrameView::LayoutWindowControlsOverlay() {
+void BrowserFrameViewWin::LayoutWindowControlsOverlay() {
   if (!web_app_frame_toolbar()) {
     return;
   }
@@ -801,7 +834,7 @@
   }
 }
 
-void GlassBrowserFrameView::LayoutClientView() {
+void BrowserFrameViewWin::LayoutClientView() {
   client_view_bounds_ = GetLocalBounds();
   int top_inset = GetTopInset(false);
   if (browser_view()->IsWindowControlsOverlayEnabled()) {
@@ -815,7 +848,7 @@
   client_view_bounds_.Inset(gfx::Insets::TLBR(top_inset, 0, 0, 0));
 }
 
-void GlassBrowserFrameView::StartThrobber() {
+void BrowserFrameViewWin::StartThrobber() {
   DCHECK(ShouldShowWindowIcon(TitlebarType::kSystem));
   if (!throbber_running_) {
     throbber_running_ = true;
@@ -827,7 +860,7 @@
   }
 }
 
-void GlassBrowserFrameView::StopThrobber() {
+void BrowserFrameViewWin::StopThrobber() {
   DCHECK(ShouldShowWindowIcon(TitlebarType::kSystem));
   if (throbber_running_) {
     throbber_running_ = false;
@@ -878,7 +911,7 @@
   }
 }
 
-void GlassBrowserFrameView::DisplayNextThrobberFrame() {
+void BrowserFrameViewWin::DisplayNextThrobberFrame() {
   throbber_frame_ = (throbber_frame_ + 1) % kThrobberIconCount;
   SendMessage(views::HWNDForWidget(frame()), WM_SETICON,
               static_cast<WPARAM>(ICON_SMALL),
@@ -886,7 +919,7 @@
 }
 
 // static
-void GlassBrowserFrameView::InitThrobberIcons() {
+void BrowserFrameViewWin::InitThrobberIcons() {
   static bool initialized = false;
   if (!initialized) {
     for (int i = 0; i < kThrobberIconCount; ++i) {
@@ -898,5 +931,5 @@
   }
 }
 
-BEGIN_METADATA(GlassBrowserFrameView, BrowserNonClientFrameView)
+BEGIN_METADATA(BrowserFrameViewWin, BrowserNonClientFrameView)
 END_METADATA
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/browser_frame_view_win.h
similarity index 87%
rename from chrome/browser/ui/views/frame/glass_browser_frame_view.h
rename to chrome/browser/ui/views/frame/browser_frame_view_win.h
index c092b31..204e8d4 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_frame_view_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_GLASS_BROWSER_FRAME_VIEW_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_GLASS_BROWSER_FRAME_VIEW_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_WIN_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_WIN_H_
 
 #include "base/memory/raw_ptr.h"
 #include "base/win/scoped_gdi_object.h"
@@ -16,18 +16,18 @@
 
 class BrowserView;
 class TabSearchBubbleHost;
-class GlassBrowserCaptionButtonContainer;
+class BrowserCaptionButtonContainer;
 
-class GlassBrowserFrameView : public BrowserNonClientFrameView,
-                              public TabIconViewModel {
+class BrowserFrameViewWin : public BrowserNonClientFrameView,
+                            public TabIconViewModel {
  public:
-  METADATA_HEADER(GlassBrowserFrameView);
+  METADATA_HEADER(BrowserFrameViewWin);
 
   // Constructs a non-client view for an BrowserFrame.
-  GlassBrowserFrameView(BrowserFrame* frame, BrowserView* browser_view);
-  GlassBrowserFrameView(const GlassBrowserFrameView&) = delete;
-  GlassBrowserFrameView& operator=(const GlassBrowserFrameView&) = delete;
-  ~GlassBrowserFrameView() override;
+  BrowserFrameViewWin(BrowserFrame* frame, BrowserView* browser_view);
+  BrowserFrameViewWin(const BrowserFrameViewWin&) = delete;
+  BrowserFrameViewWin& operator=(const BrowserFrameViewWin&) = delete;
+  ~BrowserFrameViewWin() override;
 
   // BrowserNonClientFrameView:
   bool CaptionButtonsOnLeadingEdge() const override;
@@ -71,8 +71,8 @@
 
   SkColor GetTitlebarColor() const;
 
-  const GlassBrowserCaptionButtonContainer*
-  caption_button_container_for_testing() const {
+  const BrowserCaptionButtonContainer* caption_button_container_for_testing()
+      const {
     return caption_button_container_;
   }
 
@@ -85,7 +85,7 @@
   void Layout() override;
 
  private:
-  friend class GlassBrowserCaptionButtonContainer;
+  friend class BrowserCaptionButtonContainer;
 
   // Describes the type of titlebar that a window might have; used to query
   // whether specific elements may be present.
@@ -174,7 +174,7 @@
   // The container holding the caption buttons (minimize, maximize, close, etc.)
   // May be null if the caption button container is destroyed before the frame
   // view. Always check for validity before using!
-  raw_ptr<GlassBrowserCaptionButtonContainer> caption_button_container_;
+  raw_ptr<BrowserCaptionButtonContainer> caption_button_container_;
 
   // Whether or not the window throbber is currently animating.
   bool throbber_running_ = false;
@@ -187,4 +187,4 @@
   static void InitThrobberIcons();
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_GLASS_BROWSER_FRAME_VIEW_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_WIN_H_
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
index f8cc223..b65c3cc 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h"
 
 #if BUILDFLAG(IS_WIN)
-#include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 #endif
 
 #if BUILDFLAG(IS_LINUX)
@@ -98,7 +98,7 @@
 
 #if BUILDFLAG(IS_WIN)
   if (frame->ShouldUseNativeFrame())
-    return std::make_unique<GlassBrowserFrameView>(frame, browser_view);
+    return std::make_unique<BrowserFrameViewWin>(frame, browser_view);
 #endif
   auto view = CreateOpaqueBrowserFrameView(frame, browser_view);
   view->InitViews();
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 7f4c811..01dc9657 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -526,6 +526,36 @@
     return parent()->GetAccelerator(cmd_id, accelerator);
   }
 };
+
+// TabContainerOverlayView is a view that hosts the TabStripRegionView during
+// immersive fullscreen. The TopContainerView usually draws the background for
+// the tab strip. Since the tab strip has been reparented we need to handle
+// drawing the background here.
+class TabContainerOverlayView : public views::View {
+ public:
+  METADATA_HEADER(TabContainerOverlayView);
+  explicit TabContainerOverlayView(base::WeakPtr<BrowserView> browser_view)
+      : browser_view_(std::move(browser_view)) {}
+  ~TabContainerOverlayView() override = default;
+
+  // views::View override
+  void OnPaintBackground(gfx::Canvas* canvas) override {
+    SkColor frame_color = browser_view_->frame()->GetFrameView()->GetFrameColor(
+        BrowserFrameActiveState::kUseCurrent);
+    canvas->DrawColor(frame_color);
+
+    // TODO(https://crbug.com/1414521): Support extension based themes /
+    // backgrounds.
+  }
+
+ private:
+  // The BrowserView this overlay is created for. WeakPtr is used since
+  // this view is held in a different hierarchy.
+  base::WeakPtr<BrowserView> browser_view_;
+};
+
+BEGIN_METADATA(TabContainerOverlayView, views::View)
+END_METADATA
 #endif  // BUILDFLAG(IS_MAC)
 
 }  // namespace
@@ -579,11 +609,18 @@
 
   int GetTopInsetInBrowserView() const override {
     // BrowserView should fill the full window when window controls overlay
-    // is enabled.
+    // is enabled or when immersive fullscreen with tabs is enabled.
     if (browser_view_->IsWindowControlsOverlayEnabled() ||
         browser_view_->IsBorderlessModeEnabled()) {
       return 0;
     }
+#if BUILDFLAG(IS_MAC)
+    if (base::FeatureList::IsEnabled(features::kImmersiveFullscreenTabs) &&
+        browser_view_->immersive_mode_controller()->IsEnabled()) {
+      return 0;
+    }
+#endif
+
     return browser_view_->frame()->GetTopInset() - browser_view_->y();
   }
 
@@ -679,6 +716,18 @@
             : browser_view_->GetMirroredRect(available_titlebar_area));
   }
 
+  bool ShouldLayoutTabStrip() const override {
+#if BUILDFLAG(IS_MAC)
+    // The tab strip is hosted in a separate widget in immersive fullscreen on
+    // macOS.
+    if (base::FeatureList::IsEnabled(features::kImmersiveFullscreenTabs) &&
+        browser_view_->immersive_mode_controller()->IsEnabled()) {
+      return false;
+    }
+#endif
+    return true;
+  }
+
  private:
   raw_ptr<BrowserView> browser_view_;
 };
@@ -3554,6 +3603,12 @@
   if (base::FeatureList::IsEnabled(features::kImmersiveFullscreenTabs)) {
     // Create the tab overlay widget.
     tab_overlay_widget_ = create_overlay_widget();
+    std::unique_ptr<TabContainerOverlayView> tab_overlay_view =
+        std::make_unique<TabContainerOverlayView>(
+            weak_ptr_factory_.GetWeakPtr());
+    tab_overlay_view_ = tab_overlay_view.get();
+    tab_overlay_widget_->GetRootView()->AddChildView(
+        std::move(tab_overlay_view));
 
     // TODO(https://crbug.com/1414521): Figure out how to handle multiple view
     // targeters.
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index d769d391..e24a154 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -199,6 +199,7 @@
 #if BUILDFLAG(IS_MAC)
   views::Widget* overlay_widget() { return overlay_widget_.get(); }
   views::Widget* tab_overlay_widget() { return tab_overlay_widget_.get(); }
+  views::View* tab_overlay_view() { return tab_overlay_view_.get(); }
 
   // Returns if this browser view will use immersive fullscreen mode, based
   // on the state of the two relevant base::Features, as well as the type of
@@ -1046,13 +1047,17 @@
   // Used when calling CreateMacOverlayView(). This widget owns `overlay_view_`.
   // Its content NSView will be reparented to a NSToolbarFullScreenWindow
   // during fullscreen.
-  raw_ptr<views::Widget, DanglingUntriaged> overlay_widget_;
+  raw_ptr<views::Widget, DanglingUntriaged> overlay_widget_ = nullptr;
 
   // Also used when calling CreateMacOverlayView(). This widget will host the
   // tabstrip contents. Its content NSView will be reparented to a separate
   // section of the NSToolbarFullScreenWindow allowing for the tabs to live in
   // the Titlebar.
-  raw_ptr<views::Widget, DanglingUntriaged> tab_overlay_widget_;
+  raw_ptr<views::Widget, DanglingUntriaged> tab_overlay_widget_ = nullptr;
+
+  // The hosting view of TabStripRegionView during immersive fullscreen.
+  raw_ptr<views::View, DanglingUntriaged> tab_overlay_view_ = nullptr;
+
 #endif
 
   // The Bookmark Bar View for this window. Lazily created. May be null for
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc
index c9b4becd..bb08afe 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -384,13 +384,15 @@
   vertical_layout_rect_ = browser_view->GetLocalBounds();
   int top_inset = delegate_->GetTopInsetInBrowserView();
   int top = LayoutTitleBarForWebApp(top_inset);
-  top = LayoutTabStripRegion(top);
-  if (delegate_->IsTabStripVisible()) {
-    tab_strip_->SetBackgroundOffset(tab_strip_region_view_->GetMirroredX() +
-                                    browser_view_->GetMirroredX() +
-                                    delegate_->GetThemeBackgroundXInset());
+  if (delegate_->ShouldLayoutTabStrip()) {
+    top = LayoutTabStripRegion(top);
+    if (delegate_->IsTabStripVisible()) {
+      tab_strip_->SetBackgroundOffset(tab_strip_region_view_->GetMirroredX() +
+                                      browser_view_->GetMirroredX() +
+                                      delegate_->GetThemeBackgroundXInset());
+    }
+    top = LayoutWebUITabStrip(top);
   }
-  top = LayoutWebUITabStrip(top);
   top = LayoutToolbar(top);
 
   top = LayoutBookmarkAndInfoBars(top, browser_view->y());
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_delegate.h b/chrome/browser/ui/views/frame/browser_view_layout_delegate.h
index f4217b2..20824d6 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_delegate.h
+++ b/chrome/browser/ui/views/frame/browser_view_layout_delegate.h
@@ -46,6 +46,7 @@
   virtual bool IsWindowControlsOverlayEnabled() const = 0;
   virtual void UpdateWindowControlsOverlay(
       const gfx::Rect& available_titlebar_area) const = 0;
+  virtual bool ShouldLayoutTabStrip() const = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_VIEW_LAYOUT_DELEGATE_H_
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
index a0fe278..d5c9c26 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
@@ -106,6 +106,7 @@
   void MoveWindowForFindBarIfNecessary() const override {}
   bool IsWindowControlsOverlayEnabled() const override { return false; }
   void UpdateWindowControlsOverlay(const gfx::Rect& rect) const override {}
+  bool ShouldLayoutTabStrip() const override { return true; }
 
  private:
   bool tab_strip_visible_ = true;
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm b/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
index fc865b14..f655245 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
@@ -11,19 +11,30 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/common/chrome_features.h"
+#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/views/border.h"
 #include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/view_observer.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
+
+// The width of the traffic lights. Used to layout the tab strip leaving a hole
+// for the traffic lights.
+// TODO(https://crbug.com/1414521): Get this dynamically. Unfortunately the
+// values in BrowserNonClientFrameViewMac::GetCaptionButtonInsets don't account
+// for a window with an NSToolbar.
+const int kTrafficLightsWidth = 70;
+
 class ImmersiveModeControllerMac : public ImmersiveModeController,
                                    public views::FocusChangeListener,
                                    public views::ViewObserver,
@@ -286,24 +297,48 @@
 
   // ImmersiveModeControllerMac overrides:
   void SetEnabled(bool enabled) override;
+  void OnViewBoundsChanged(views::View* observed_view) override;
 
  private:
+  int tab_widget_height_ = 0;
 };
 
 void ImmersiveModeTabbedControllerMac::SetEnabled(bool enabled) {
   BrowserView* browser_view = ImmersiveModeControllerMac::browser_view();
   if (enabled) {
-    browser_view->tab_overlay_widget()->GetRootView()->AddChildView(
-        browser_view->tab_strip_region_view());
+    tab_widget_height_ = browser_view->tab_strip_region_view()->height();
+    tab_widget_height_ += static_cast<BrowserNonClientFrameViewMac*>(
+                              browser_view->frame()->GetFrameView())
+                              ->GetTopInset(false);
+    // Without this -1 the tabs sit 1px too high. I assume this is because in
+    // fullscreen there is no resize handle.
+    tab_widget_height_ -= 1;
 
-    // TODO(https://crbug.com/1414521): The +8 height here is just a
-    // placeholder. Instead we need to move the top_container background to the
-    // tab_overlay_widget.
-    gfx::Size tab_region_size = browser_view->tab_strip_region_view()->size();
-    tab_region_size.set_height(tab_region_size.height() + 8);
-    browser_view->tab_overlay_widget()->SetSize(tab_region_size);
+    // TODO(https://crbug.com/1414521): The |tab_overlay_widget()| draws
+    // underneath the traffic lights via an NSTitlebarViewController with
+    // NSLayoutAttributeTrailing layout. In order to propagate all mouse and
+    // keyboard events from AppKit back to Views the |tab_overlay_widget()|
+    // needs to be placed at the same location on screen as the
+    // NSTitlebarViewController. 0,0 is the correct location for the input to
+    // line up with the view, however this causes mouse actions to not make it
+    // to the traffic lights. For now the |tab_overlay_widget()| has been
+    // ordered behind the AppKit fullscreen window which hosts the traffic
+    // lights. This allows for interaction with the traffic lights and tab strip
+    // but child widgets of |tab_overlay_widget()| appear underneath the
+    // toolbar. Find a solution.
+    browser_view->tab_overlay_widget()->SetBounds(
+        gfx::Rect(0, 0, browser_view->top_container()->size().width(),
+                  tab_widget_height_));
     browser_view->tab_overlay_widget()->Show();
 
+    // Inset the start of |tab_strip_region_view()| by |kTrafficLightsWidth|.
+    // This will leave a hole for the traffic light to appear.
+    browser_view->tab_overlay_view()->AddChildView(
+        browser_view->tab_strip_region_view());
+    gfx::Insets insets = gfx::Insets::TLBR(0, kTrafficLightsWidth, 0, 0);
+    browser_view->tab_strip_region_view()->SetBorder(
+        views::CreateEmptyBorder(insets));
+
     views::NativeWidgetMacNSWindowHost* tab_overlay_host =
         views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(
             browser_view->tab_overlay_widget()->GetNativeWindow());
@@ -311,12 +346,25 @@
     ImmersiveModeControllerMac::SetEnabled(enabled);
   } else {
     browser_view->tab_overlay_widget()->Hide();
+    browser_view->tab_strip_region_view()->SetBorder(nullptr);
     browser_view->top_container()->AddChildViewAt(
         browser_view->tab_strip_region_view(), 0);
     ImmersiveModeControllerMac::SetEnabled(enabled);
   }
 }
 
+void ImmersiveModeTabbedControllerMac::OnViewBoundsChanged(
+    views::View* observed_view) {
+  // Resize the width of |tab_overlay_view()| and |tab_overlay_widget()|.
+  BrowserView* browser_view = ImmersiveModeControllerMac::browser_view();
+  gfx::Size new_size(observed_view->size().width(), tab_widget_height_);
+  browser_view->tab_overlay_widget()->SetSize(new_size);
+  browser_view->tab_overlay_view()->SetSize(new_size);
+  browser_view->tab_strip_region_view()->SetSize(gfx::Size(
+      new_size.width(), browser_view->tab_strip_region_view()->height()));
+  ImmersiveModeControllerMac::OnViewBoundsChanged(observed_view);
+}
+
 std::unique_ptr<ImmersiveModeController> CreateImmersiveModeControllerMac() {
   if (base::FeatureList::IsEnabled(features::kImmersiveFullscreenTabs)) {
     return std::make_unique<ImmersiveModeTabbedControllerMac>();
diff --git a/chrome/browser/ui/views/frame/windows_caption_button.cc b/chrome/browser/ui/views/frame/windows_caption_button.cc
index 10b7c49..ff6450c 100644
--- a/chrome/browser/ui/views/frame/windows_caption_button.cc
+++ b/chrome/browser/ui/views/frame/windows_caption_button.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/frame/window_frame_util.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
 #include "chrome/grit/theme_resources.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/theme_provider.h"
@@ -23,7 +23,7 @@
 
 WindowsCaptionButton::WindowsCaptionButton(
     PressedCallback callback,
-    GlassBrowserFrameView* frame_view,
+    BrowserFrameViewWin* frame_view,
     ViewID button_type,
     const std::u16string& accessible_name)
     : views::Button(std::move(callback)),
diff --git a/chrome/browser/ui/views/frame/windows_caption_button.h b/chrome/browser/ui/views/frame/windows_caption_button.h
index d025b0b1..7fb3185 100644
--- a/chrome/browser/ui/views/frame/windows_caption_button.h
+++ b/chrome/browser/ui/views/frame/windows_caption_button.h
@@ -14,13 +14,13 @@
 #include "ui/gfx/canvas.h"
 #include "ui/views/controls/button/button.h"
 
-class GlassBrowserFrameView;
+class BrowserFrameViewWin;
 
 class WindowsCaptionButton : public views::Button {
  public:
   METADATA_HEADER(WindowsCaptionButton);
   WindowsCaptionButton(PressedCallback callback,
-                       GlassBrowserFrameView* frame_view,
+                       BrowserFrameViewWin* frame_view,
                        ViewID button_type,
                        const std::u16string& accessible_name);
   WindowsCaptionButton(const WindowsCaptionButton&) = delete;
@@ -52,7 +52,7 @@
   // Paints the minimize/maximize/restore/close icon for the button.
   void PaintSymbol(gfx::Canvas* canvas);
 
-  raw_ptr<GlassBrowserFrameView> frame_view_;
+  raw_ptr<BrowserFrameViewWin> frame_view_;
   std::unique_ptr<Windows10IconPainter> icon_painter_;
   ViewID button_type_;
 };
diff --git a/chrome/browser/ui/views/frame/windows_tab_search_caption_button.cc b/chrome/browser/ui/views/frame/windows_tab_search_caption_button.cc
index 6d68998..868958e 100644
--- a/chrome/browser/ui/views/frame/windows_tab_search_caption_button.cc
+++ b/chrome/browser/ui/views/frame/windows_tab_search_caption_button.cc
@@ -6,8 +6,8 @@
 
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
 #include "chrome/browser/ui/views/tab_search_bubble_host.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -15,7 +15,7 @@
 #include "ui/views/view_class_properties.h"
 
 WindowsTabSearchCaptionButton::WindowsTabSearchCaptionButton(
-    GlassBrowserFrameView* frame_view,
+    BrowserFrameViewWin* frame_view,
     ViewID button_type,
     const std::u16string& accessible_name)
     : WindowsCaptionButton(views::Button::PressedCallback(),
diff --git a/chrome/browser/ui/views/frame/windows_tab_search_caption_button.h b/chrome/browser/ui/views/frame/windows_tab_search_caption_button.h
index 2aa061fa..678513c8 100644
--- a/chrome/browser/ui/views/frame/windows_tab_search_caption_button.h
+++ b/chrome/browser/ui/views/frame/windows_tab_search_caption_button.h
@@ -8,13 +8,13 @@
 #include "chrome/browser/ui/views/frame/windows_caption_button.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 
-class GlassBrowserFrameView;
+class BrowserFrameViewWin;
 class TabSearchBubbleHost;
 
 class WindowsTabSearchCaptionButton : public WindowsCaptionButton {
  public:
   METADATA_HEADER(WindowsTabSearchCaptionButton);
-  WindowsTabSearchCaptionButton(GlassBrowserFrameView* frame_view,
+  WindowsTabSearchCaptionButton(BrowserFrameViewWin* frame_view,
                                 ViewID button_type,
                                 const std::u16string& accessible_name);
   WindowsTabSearchCaptionButton(const WindowsTabSearchCaptionButton&) = delete;
diff --git a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
index a46a0bfd..5e49c1c 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/auto_reset.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
 #include "chrome/browser/download/download_request_limiter.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/content_settings/content_setting_image_model.h"
@@ -30,6 +33,7 @@
 #include "components/permissions/permission_ui_selector.h"
 #include "components/permissions/request_type.h"
 #include "components/permissions/test/mock_permission_request.h"
+#include "content/public/browser/navigation_entry.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "ui/events/event.h"
@@ -75,13 +79,31 @@
   QuietUiReason simulated_reason_for_quiet_ui_;
 };
 
+// An override that returns a fake URL for every blocked popup, so the UI
+// displays consistent strings for pixel tests.
+class TestPopupNavigationDelegate : public ChromePopupNavigationDelegate {
+ public:
+  using ChromePopupNavigationDelegate::ChromePopupNavigationDelegate;
+
+  // ChromePopupNavigationDelegate:
+  GURL GetURL() override { return GURL("http://blocked-popup/"); }
+};
+
+std::unique_ptr<blocked_content::PopupNavigationDelegate>
+CreateTestPopupNavigationDelegate(NavigateParams params) {
+  return std::make_unique<TestPopupNavigationDelegate>(std::move(params));
+}
+
 }  // namespace
 
 using ImageType = ContentSettingImageModel::ImageType;
 
 class ContentSettingBubbleDialogTest : public DialogBrowserTest {
  public:
-  ContentSettingBubbleDialogTest() {
+  ContentSettingBubbleDialogTest()
+      : resetter_(&ChromeContentBrowserClient::
+                      GetPopupNavigationDelegateFactoryForTesting(),
+                  &CreateTestPopupNavigationDelegate) {
     scoped_feature_list_.InitWithFeatures(
         {features::kQuietNotificationPrompts},
         {permissions::features::kPermissionQuietChip});
@@ -102,6 +124,8 @@
   void ShowUi(const std::string& name) override;
 
  private:
+  base::AutoReset<ChromeContentBrowserClient::PopupNavigationDelegateFactory>
+      resetter_;
   base::test::ScopedFeatureList scoped_feature_list_;
   absl::optional<permissions::MockPermissionRequest>
       notification_permission_request_;
@@ -157,6 +181,9 @@
           blocked_content::PopupBlockerTabHelper::FromWebContents(web_contents);
       // popup-many-10.html should generate 10 blocked popups.
       EXPECT_EQ(10u, helper->GetBlockedPopupsCount());
+      // Set a fake URL so the UI displays a consistent string for pixel tests.
+      web_contents->GetController().GetVisibleEntry()->SetVirtualURL(
+          GURL("http://popuptest/"));
       break;
     }
     case ContentSettingsType::PROTOCOL_HANDLERS:
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index d549d83..f8e5eefc 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -1098,15 +1098,6 @@
   return match_selection_timestamp();
 }
 
-void LocationBarView::AcceptInput() {
-  AcceptInput(base::TimeTicks());
-}
-
-void LocationBarView::AcceptInput(base::TimeTicks match_selection_timestamp) {
-  omnibox_view_->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB,
-                                      match_selection_timestamp);
-}
-
 void LocationBarView::FocusSearch() {
   // This is called by keyboard accelerator, so it's user-initiated.
   omnibox_view_->SetFocus(/*is_user_initiated=*/true);
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index f7d7965..8057f45 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -320,8 +320,6 @@
   WindowOpenDisposition GetWindowOpenDisposition() const override;
   ui::PageTransition GetPageTransition() const override;
   base::TimeTicks GetMatchSelectionTimestamp() const override;
-  void AcceptInput() override;
-  void AcceptInput(base::TimeTicks match_selection_timestamp) override;
   void FocusSearch() override;
   void UpdateContentSettingsIcons() override;
   void SaveStateToContents(content::WebContents* contents) override;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 0eaafaf..a60f0b6 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -416,8 +416,8 @@
 
 void OmniboxResultView::ButtonPressed(OmniboxPopupSelection::LineState state,
                                       const ui::Event& event) {
-  model_->TriggerPopupSelectionAction(
-      OmniboxPopupSelection(model_index_, state), event.time_stamp());
+  model_->OpenSelection(OmniboxPopupSelection(model_index_, state),
+                        event.time_stamp());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
index 7c05ce6..79cfdc9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
@@ -7,6 +7,7 @@
 #include "base/functional/bind.h"
 #include "base/i18n/case_conversion.h"
 #include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
@@ -129,7 +130,7 @@
     return true;
   }
   void OnMouseReleased(const ui::MouseEvent& event) override {
-    row_view_->model_->TriggerPopupSelectionAction(GetHeaderSelection());
+    row_view_->model_->OpenSelection(GetHeaderSelection(), event.time_stamp());
   }
   void OnMouseEntered(const ui::MouseEvent& event) override { UpdateUI(); }
   void OnMouseExited(const ui::MouseEvent& event) override { UpdateUI(); }
@@ -208,7 +209,7 @@
 
  private:
   void HeaderToggleButtonPressed() {
-    row_view_->model_->TriggerPopupSelectionAction(GetHeaderSelection());
+    row_view_->model_->OpenSelection(GetHeaderSelection(), base::TimeTicks());
     // The PrefChangeRegistrar will update the actual button toggle state.
   }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
index 5a8fda3..42f7e06 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
@@ -352,8 +352,7 @@
   } else {
     WindowOpenDisposition disposition =
         ui::DispositionFromEventFlags(event.flags());
-    model_->TriggerPopupSelectionAction(selection, event.time_stamp(),
-                                        disposition);
+    model_->OpenSelection(selection, event.time_stamp(), disposition);
   }
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 06b3dea..bd53c0b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -1587,13 +1587,8 @@
       } else if (shift) {
         disposition = WindowOpenDisposition::NEW_WINDOW;
       }
-      if (model()->PopupIsOpen() &&
-          model()->TriggerPopupSelectionAction(
-              model()->GetPopupSelection(), event.time_stamp(), disposition)) {
-        return true;
-      } else {
-        model()->AcceptInput(disposition, event.time_stamp());
-      }
+      model()->OpenSelection(model()->GetPopupSelection(), event.time_stamp(),
+                             disposition);
       return true;
     }
     case ui::VKEY_ESCAPE:
@@ -1699,10 +1694,8 @@
       if (model()->PopupIsOpen()) {
         OmniboxPopupSelection selection = model()->GetPopupSelection();
         if (selection.IsButtonFocused() && !control && !alt && !shift) {
-          if (model()->TriggerPopupSelectionAction(selection,
-                                                   event.time_stamp())) {
-            return true;
-          }
+          model()->OpenSelection(selection, event.time_stamp());
+          return true;
         }
       }
       break;
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc
index c7e2ac4..7a7829d 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -714,6 +714,9 @@
       [](PasswordSaveUpdateView* view,
          std::unique_ptr<CloseOnDeactivatePin> pin, bool reveal) {
         view->password_dropdown_->RevealPasswords(reveal);
+        // This is necessary on Windows since the bubble isn't activated again
+        // after the conlusion of the auth flow.
+        view->GetWidget()->Activate();
         // Delay the destruction of `pin` for 1 sec to make sure the bubble
         // remains open till the OS closes the authentication dialog and
         // reactivates the bubble.
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc
index 435cf0a..fc13bf5b 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc
@@ -35,6 +35,7 @@
 #include "components/user_education/views/help_bubble_view.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
+#include "third_party/blink/public/common/switches.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -152,6 +153,13 @@
   HighEfficiencyDiscardPolicyInteractiveTest() = default;
   ~HighEfficiencyDiscardPolicyInteractiveTest() override = default;
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    HighEfficiencyInteractiveTest::SetUpCommandLine(command_line);
+    // Some builders are flaky due to slower loading interacting with
+    // deferred commits.
+    command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
+  }
+
   auto PressKeyboard() {
     return Do(base::BindLambdaForTesting([=]() {
       ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_A, false,
@@ -220,9 +228,8 @@
 
 // Check that a form in the background but was interacted by the user
 // won't be discarded
-// TODO(crbug.com/1415833): Re-enable this test
 IN_PROC_BROWSER_TEST_F(HighEfficiencyDiscardPolicyInteractiveTest,
-                       DISABLED_TabWithFormNotDiscarded) {
+                       TabWithFormNotDiscarded) {
   DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kInputIsFocused);
   DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kInputValueIsUpated);
   const DeepQuery input_text_box = {"#value"};
@@ -241,11 +248,6 @@
   input_value_updated.test_function = "(el) => { return el.value === 'a'; }";
 
   RunTestSequence(
-      SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
-                              "Some Linux window managers do not allow "
-                              "programmatically raising/activating windows. "
-                              "This skips the rest of the test."),
-      ActivateSurface(kBrowserViewElementId), FlushEvents(),
       InstrumentTab(kFirstTabContents, 0),
       NavigateWebContents(kFirstTabContents, GetURL("/form_search.html")),
 
@@ -254,7 +256,7 @@
 
       // Wait until the input text box is focused and simulate typing a letter
       ExecuteJsAt(kFirstTabContents, input_text_box,
-                  "(el) => { el.select(); }"),
+                  "(el) => { el.focus(); el.select(); }"),
       WaitForStateChange(kFirstTabContents, std::move(input_is_focused)),
       PressKeyboard(),
       WaitForStateChange(kFirstTabContents, std::move(input_value_updated)),
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc
index c1f20c0..673d4f9 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc
@@ -6,70 +6,14 @@
 
 #include <vector>
 
-#include "base/containers/contains.h"
-#include "base/memory/raw_ptr.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
 #include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h"
 #include "chrome/common/accessibility/read_anything.mojom.h"
 #include "chrome/common/accessibility/read_anything_constants.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/ax_action_data.h"
 
-class ReadAnythingWebContentsObserver
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<ReadAnythingWebContentsObserver> {
- public:
-  ReadAnythingWebContentsObserver(const ReadAnythingWebContentsObserver&) =
-      delete;
-  ReadAnythingWebContentsObserver& operator=(
-      const ReadAnythingWebContentsObserver&) = delete;
-  ~ReadAnythingWebContentsObserver() override = default;
-
-  // content::WebContentsObserver:
-  void AccessibilityEventReceived(
-      const content::AXEventNotificationDetails& details) override {
-    if (controller_) {
-      controller_->AccessibilityEventReceived(details);
-    }
-  }
-
-  void WebContentsDestroyed() override {
-    if (controller_) {
-      controller_->WebContentsDestroyed(web_contents());
-    }
-  }
-
-  // This causes AXTreeSerializer to reset and send accessibility events of the
-  // AXTree when it is re-serialized.
-  void EnableAccessibility() {
-    // TODO(crbug.com/1266555): Only enable kReadAnythingAXMode.
-    web_contents()->EnableWebContentsOnlyAccessibilityMode();
-  }
-
-  void SetController(ReadAnythingController* controller) {
-    controller_ = controller;
-  }
-
- private:
-  friend class content::WebContentsUserData<ReadAnythingWebContentsObserver>;
-
-  explicit ReadAnythingWebContentsObserver(content::WebContents* web_contents)
-      : content::WebContentsObserver(web_contents),
-        content::WebContentsUserData<ReadAnythingWebContentsObserver>(
-            *web_contents) {}
-
-  raw_ptr<ReadAnythingController> controller_ = nullptr;
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(ReadAnythingWebContentsObserver);
-
 ReadAnythingController::ReadAnythingController(ReadAnythingModel* model,
                                                Browser* browser)
     : model_(model), browser_(browser) {
@@ -79,18 +23,12 @@
 
 ReadAnythingController::~ReadAnythingController() {
   TabStripModelObserver::StopObservingAll(this);
-  for (auto* web_contents : AllTabContentses()) {
-    ReadAnythingWebContentsObserver* observer =
-        ReadAnythingWebContentsObserver::FromWebContents(web_contents);
-    if (observer) {
-      observer->SetController(nullptr);
-    }
-  }
+  Observe(nullptr);
 }
 
 void ReadAnythingController::Activate(bool active) {
   active_ = active;
-  NotifyActiveAXTreeIDChanged();
+  OnActiveWebContentsChanged();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -196,11 +134,12 @@
         screen_ai::ScreenAIInstallState::GetInstance());
   }
 #endif
-  NotifyActiveAXTreeIDChanged();
+  OnActiveWebContentsChanged();
 }
 
 void ReadAnythingController::OnUIDestroyed() {
   ui_ready_ = false;
+  Observe(nullptr);
 }
 
 void ReadAnythingController::OnLinkClicked(const ui::AXTreeID& target_tree_id,
@@ -250,7 +189,7 @@
     return;
   }
   if (selection.active_tab_changed()) {
-    NotifyActiveAXTreeIDChanged();
+    OnActiveWebContentsChanged();
   }
 }
 
@@ -270,33 +209,48 @@
   model_->AccessibilityEventReceived(details);
 }
 
-void ReadAnythingController::WebContentsDestroyed(
-    content::WebContents* web_contents) {
+void ReadAnythingController::WebContentsDestroyed() {
   content::RenderFrameHost* render_frame_host =
-      web_contents->GetPrimaryMainFrame();
+      web_contents()->GetPrimaryMainFrame();
   if (!render_frame_host)
     return;
   ui::AXTreeID tree_id = render_frame_host->GetAXTreeID();
   model_->OnAXTreeDestroyed(tree_id);
 }
 
-void ReadAnythingController::NotifyActiveAXTreeIDChanged() {
+void ReadAnythingController::OnActiveWebContentsChanged() {
+  // TODO(crbug.com/1266555): Disable accessibility.and stop observing events
+  // on the now inactive tab. But make sure that we don't disable it for
+  // assistive technology users. Some options here are:
+  // 1. Cache the current AXMode of the active web contents before enabling
+  //    accessibility, and reset the mode to that mode when the tab becomes
+  //    inactive.
+  // 2. Set an AXContext on the web contents with web contents only mode
+  //    enabled.
+
   ui::AXTreeID tree_id = ui::AXTreeIDUnknown();
   ukm::SourceId ukm_source_id = ukm::kInvalidSourceId;
+  content::WebContents* web_contents = nullptr;
   if (active_) {
-    content::WebContents* web_contents =
-        browser_->tab_strip_model()->GetActiveWebContents();
-    if (!web_contents) {
-      return;
+    web_contents = browser_->tab_strip_model()->GetActiveWebContents();
+    if (web_contents) {
+      content::RenderFrameHost* render_frame_host =
+          web_contents->GetPrimaryMainFrame();
+      if (render_frame_host) {
+        tree_id = render_frame_host->GetAXTreeID();
+        ukm_source_id = render_frame_host->GetPageUkmSourceId();
+      }
     }
-    content::RenderFrameHost* render_frame_host =
-        web_contents->GetPrimaryMainFrame();
-    if (!render_frame_host) {
-      return;
-    }
-    tree_id = render_frame_host->GetAXTreeID();
-    ukm_source_id = render_frame_host->GetPageUkmSourceId();
-    ObserveAccessibilityEventsOnActiveTab();
+  }
+
+  Observe(web_contents);
+  // Enable accessibility for the top level render frame and all descendants.
+  // This causes AXTreeSerializer to reset and send accessibility events of
+  // the AXTree when it is re-serialized.
+  // TODO(crbug.com/1266555): Only enable kReadAnythingAXMode while still
+  // causing the reset.
+  if (web_contents) {
+    web_contents->EnableWebContentsOnlyAccessibilityMode();
   }
   model_->OnActiveAXTreeIDChanged(tree_id, ukm_source_id);
 }
@@ -311,26 +265,3 @@
   model_->ScreenAIServiceReady();
 }
 #endif
-
-void ReadAnythingController::ObserveAccessibilityEventsOnActiveTab() {
-  content::WebContents* web_contents =
-      browser_->tab_strip_model()->GetActiveWebContents();
-  if (!web_contents) {
-    return;
-  }
-  // CreateForWebContents is no-op if an observer already exists.
-  ReadAnythingWebContentsObserver::CreateForWebContents(web_contents);
-  ReadAnythingWebContentsObserver* observer =
-      ReadAnythingWebContentsObserver::FromWebContents(web_contents);
-  observer->SetController(this);
-  observer->EnableAccessibility();
-
-  // TODO(crbug.com/1266555): Disable accessibility.and stop observing events
-  // on the now inactive tab. But make sure that we don't disable it for
-  // assistive technology users. Some options here are:
-  // 1. Cache the current AXMode of the active web contents before enabling
-  //    accessibility, and reset the mode to that mode when the tab becomes
-  //    inactive.
-  // 2. Set an AXContext on the web contents with web contents only mode
-  //    enabled.
-}
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h
index 00c2085..14b4432 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h
@@ -40,7 +40,8 @@
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
                                public screen_ai::ScreenAIInstallState::Observer,
 #endif
-                               public TabStripModelObserver {
+                               public TabStripModelObserver,
+                               public content::WebContentsObserver {
  public:
   ReadAnythingController(ReadAnythingModel* model, Browser* browser);
   ReadAnythingController(const ReadAnythingController&) = delete;
@@ -52,14 +53,6 @@
   void Activate(bool active);
   bool IsActiveForTesting() { return active_; }
 
-  // Called by ReadAnythingWebContentsObserver. Sends |details| to the WebUI.
-  void AccessibilityEventReceived(
-      const content::AXEventNotificationDetails& details);
-
-  // Called by ReadAnythingWebContentsObserver. Notifies the WebUI that the
-  // AXTree for |web_contents| has been destroyed.
-  void WebContentsDestroyed(content::WebContents* web_contents);
-
  private:
   friend class ReadAnythingControllerTest;
 
@@ -94,17 +87,19 @@
       const TabStripSelectionChange& selection) override;
   void OnTabStripModelDestroyed(TabStripModel* tab_strip_model) override;
 
-  // Notifies the model that the AXTreeID has changed.
-  void NotifyActiveAXTreeIDChanged();
+  // content::WebContentsObserver:
+  void AccessibilityEventReceived(
+      const content::AXEventNotificationDetails& details) override;
+  void WebContentsDestroyed() override;
 
-  // Create a web contents observer for the active tab and enable web
-  // contents-only accessibility. This causes AXTreeSerializer to reset and send
-  // accessibility events of the AXTree when it is re-serialized. The WebUI
-  // receives these events and stores a copy of each web contents' AXTree. If
-  // the UI was destroyed, it stops receiving events. OnUIReady is called when
-  // it is re-created, indicating that it needs to restore its copy of each
-  // web contents' AXTree.
-  void ObserveAccessibilityEventsOnActiveTab();
+  // When the active web contents changes (or the UI becomes active):
+  // 1. Begins observing the web contents of the active tab and enables web
+  //    contents-only accessibility on that web contents. This causes
+  //    AXTreeSerializer to reset and send accessibility events of the AXTree
+  //    when it is re-serialized. The WebUI receives these events and stores a
+  //    copy of the web contents' AXTree.
+  // 2. Notifies the model that the AXTreeID has changed.
+  void OnActiveWebContentsChanged();
 
   const raw_ptr<ReadAnythingModel> model_;
 
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index 46b0597..f7ef943b0 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -267,14 +267,15 @@
 }
 
 void WebAppBrowserController::OnRelationshipCheckComplete(
-    digital_asset_links::RelationshipCheckResult result) {
+    content_relationship_verification::RelationshipCheckResult result) {
   bool should_show_cct = false;
   switch (result) {
-    case digital_asset_links::RelationshipCheckResult::kSuccess:
+    case content_relationship_verification::RelationshipCheckResult::kSuccess:
       should_show_cct = false;
       break;
-    case digital_asset_links::RelationshipCheckResult::kFailure:
-    case digital_asset_links::RelationshipCheckResult::kNoConnection:
+    case content_relationship_verification::RelationshipCheckResult::kFailure:
+    case content_relationship_verification::RelationshipCheckResult::
+        kNoConnection:
       should_show_cct = true;
       break;
   }
@@ -670,9 +671,9 @@
 void WebAppBrowserController::PerformDigitalAssetLinkVerification(
     Browser* browser) {
 #if BUILDFLAG(IS_CHROMEOS)
-  asset_link_handler_ =
-      std::make_unique<digital_asset_links::DigitalAssetLinksHandler>(
-          browser->profile()->GetURLLoaderFactory());
+  asset_link_handler_ = std::make_unique<
+      content_relationship_verification::DigitalAssetLinksHandler>(
+      browser->profile()->GetURLLoaderFactory());
   is_verified_ = absl::nullopt;
 #endif
 
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.h b/chrome/browser/ui/web_applications/web_app_browser_controller.h
index 09bfc87..0727bd5 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.h
@@ -28,7 +28,7 @@
 #include "ui/base/models/image_model.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
-#include "components/digital_asset_links/digital_asset_links_handler.h"  // nogncheck
+#include "components/content_relationship_verification/digital_asset_links_handler.h"  // nogncheck
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -44,7 +44,7 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 class DigitalAssetLinksHandler;
 }
 
@@ -154,7 +154,7 @@
       const std::string& package_name,
       const std::string& fingerprint);
   void OnRelationshipCheckComplete(
-      digital_asset_links::RelationshipCheckResult result);
+      content_relationship_verification::RelationshipCheckResult result);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -184,7 +184,7 @@
   // Only used for web-only TWAs installed through the Play Store.
   absl::optional<bool> is_verified_;
 
-  std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler>
+  std::unique_ptr<content_relationship_verification::DigitalAssetLinksHandler>
       asset_link_handler_;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
index 71166c7..86e7a022 100644
--- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
+++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
@@ -70,7 +70,21 @@
       override {
     return CloneMojomVector(pointing_sticks_);
   }
-
+  const ::ash::mojom::KeyboardSettings* GetKeyboardSettings(
+      DeviceId id) override {
+    return nullptr;
+  }
+  const ::ash::mojom::TouchpadSettings* GetTouchpadSettings(
+      DeviceId id) override {
+    return nullptr;
+  }
+  const ::ash::mojom::MouseSettings* GetMouseSettings(DeviceId id) override {
+    return nullptr;
+  }
+  const ::ash::mojom::PointingStickSettings* GetPointingStickSettings(
+      DeviceId id) override {
+    return nullptr;
+  }
   void SetKeyboardSettings(
       DeviceId id,
       const ::ash::mojom::KeyboardSettings& settings) override {}
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes.mojom b/chrome/browser/ui/webui/side_panel/user_notes/user_notes.mojom
index cf7aead..68eaeb5 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes.mojom
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes.mojom
@@ -87,6 +87,10 @@
   // Called when the sort option has been updated and the profile pref should be
   // updated.
   SetSortOrder(bool sort_by_newest);
+
+  // Return whether there are any notes in any pages.
+  // Used to determine if the empty state UI should be shown.
+  HasNotesInAnyPages() => (bool has_notes);
 };
 
 // WebUI-side handler for requests from the browser.
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc
index 8c28c900..f19d851f 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc
@@ -257,6 +257,22 @@
   }
 }
 
+void UserNotesPageHandler::HasNotesInAnyPages(
+    HasNotesInAnyPagesCallback callback) {
+  // TODO(crbug.com/1419697) Implement a more efficient API to retrieve number
+  // of powers for a specific type instead of using GetPowerOverviewsForType
+  service_->GetPowerOverviewsForType(
+      sync_pb::PowerBookmarkSpecifics::POWER_TYPE_NOTE,
+      base::BindOnce(
+          [](HasNotesInAnyPagesCallback callback,
+             std::vector<std::unique_ptr<power_bookmarks::PowerOverview>>
+                 power_overviews) {
+            bool has_notes = power_overviews.size() > 0;
+            std::move(callback).Run(has_notes);
+          },
+          std::move(callback)));
+}
+
 void UserNotesPageHandler::OnSortByNewestPrefChanged() {
   PrefService* pref_service = profile_->GetPrefs();
   if (pref_service) {
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.h b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.h
index 1ab5da0..ac08090a9 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.h
@@ -62,6 +62,7 @@
       const ::GURL& url,
       ui::mojom::ClickModifiersPtr click_modifiers) override;
   void SetSortOrder(bool sort_by_newest) override;
+  void HasNotesInAnyPages(HasNotesInAnyPagesCallback callback) override;
 
   void OnSortByNewestPrefChanged();
 
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc
index b06ce61f..ffb63b50 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc
@@ -227,4 +227,12 @@
   ASSERT_EQ(GURL(u"https://newurl4"), handler()->GetCurrentTabUrlForTesting());
 }
 
+TEST_F(UserNotesPageHandlerTest, HasNotesOnAnyPages) {
+  side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
+  ASSERT_EQ(false, waiter.HasNotesInAnyPages());
+  handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
+  ASSERT_TRUE(waiter.NewNoteFinished("note1"));
+  ASSERT_EQ(true, waiter.HasNotesInAnyPages());
+}
+
 }  // namespace
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index 93b7936..5884eb27 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -650,8 +650,7 @@
     const device::FidoAuthenticator& authenticator) {
   if (!authenticator.AuthenticatorTransport()) {
 #if BUILDFLAG(IS_WIN)
-    DCHECK_EQ(authenticator.GetType(),
-              device::FidoAuthenticator::Type::kWinNative);
+    DCHECK_EQ(authenticator.GetType(), device::AuthenticatorType::kWinNative);
 #endif  // BUILDFLAG(IS_WIN)
     return;
   }
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index c0f33857..40f3750e 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -548,7 +548,7 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  if (authenticator->GetType() == device::FidoAuthenticator::Type::kWinNative &&
+  if (authenticator->GetType() == device::AuthenticatorType::kWinNative &&
       static_cast<const device::WinWebAuthnApiAuthenticator*>(authenticator)
           ->ShowsPrivacyNotice()) {
     // The OS' native API includes an attestation prompt.
diff --git a/chrome/browser/win/conflicts/incompatible_applications_updater.cc b/chrome/browser/win/conflicts/incompatible_applications_updater.cc
index 873b80a9..1f3e92e 100644
--- a/chrome/browser/win/conflicts/incompatible_applications_updater.cc
+++ b/chrome/browser/win/conflicts/incompatible_applications_updater.cc
@@ -68,40 +68,33 @@
   if (!value.is_dict())
     return nullptr;
 
-  const base::Value* registry_is_hkcu_value =
-      value.FindKeyOfType("registry_is_hkcu", base::Value::Type::BOOLEAN);
-  const base::Value* registry_key_path_value =
-      value.FindKeyOfType("registry_key_path", base::Value::Type::STRING);
-  const base::Value* registry_wow64_access_value =
-      value.FindKeyOfType("registry_wow64_access", base::Value::Type::INTEGER);
-  const base::Value* allow_load_value =
-      value.FindKeyOfType("allow_load", base::Value::Type::BOOLEAN);
-  const base::Value* type_value =
-      value.FindKeyOfType("type", base::Value::Type::INTEGER);
-  const base::Value* message_url_value =
-      value.FindKeyOfType("message_url", base::Value::Type::STRING);
+  const base::Value::Dict& dict = value.GetDict();
+  absl::optional<bool> registry_is_hkcu = dict.FindBool("registry_is_hkcu");
+  const std::string* registry_key_path = dict.FindString("registry_key_path");
+  absl::optional<int> registry_wow64_access =
+      dict.FindInt("registry_wow64_access");
+  absl::optional<bool> allow_load = dict.FindBool("allow_load");
+  absl::optional<int> type = dict.FindInt("type");
+  const std::string* message_url = dict.FindString("message_url");
 
   // All of the above are required for a valid application.
-  if (!registry_is_hkcu_value || !registry_key_path_value ||
-      !registry_wow64_access_value || !allow_load_value || !type_value ||
-      !message_url_value) {
+  if (!registry_is_hkcu || !registry_key_path || !registry_wow64_access ||
+      !allow_load || !type || !message_url) {
     return nullptr;
   }
 
   InstalledApplications::ApplicationInfo application_info = {
       base::UTF8ToWide(name),
-      registry_is_hkcu_value->GetBool() ? HKEY_CURRENT_USER
-                                        : HKEY_LOCAL_MACHINE,
-      base::UTF8ToWide(registry_key_path_value->GetString()),
-      static_cast<REGSAM>(registry_wow64_access_value->GetInt())};
+      *registry_is_hkcu ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
+      base::UTF8ToWide(*registry_key_path),
+      static_cast<REGSAM>(*registry_wow64_access)};
 
   auto blocklist_action =
       std::make_unique<chrome::conflicts::BlocklistAction>();
-  blocklist_action->set_allow_load(allow_load_value->GetBool());
+  blocklist_action->set_allow_load(*allow_load);
   blocklist_action->set_message_type(
-      static_cast<chrome::conflicts::BlocklistMessageType>(
-          type_value->GetInt()));
-  blocklist_action->set_message_url(message_url_value->GetString());
+      static_cast<chrome::conflicts::BlocklistMessageType>(*type));
+  blocklist_action->set_message_url(*message_url);
 
   return std::make_unique<
       IncompatibleApplicationsUpdater::IncompatibleApplication>(
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index fd1f0f6e..11c4b4f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1677477161-fa3e7882d465aefebf6c40416ffc5625a24594ba.profdata
+chrome-linux-main-1677520762-483400653117e6cccd8f74772be757e2dc79c93a.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 9b0af3e..a691573c 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1677506397-e5ba997bd0152fa9c3a18d96e42eb7a351e61109.profdata
+chrome-mac-arm-main-1677520762-d8987ca4a030e8e7e0de8f88fb0384bc32a1bfb7.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 04aa0c5..7b2db968 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1677499066-24efb56f6d80b5279656911e7050b6ea6bc97ba9.profdata
+chrome-mac-main-1677520762-2d7666fce4fb5f853778434582b9d11d4d31bb2a.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 7e34940..f1d291b 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1677499066-cacaed160a22e02d39505b344ef512a64c4aed13.profdata
+chrome-win32-main-1677520818-ff46d4d8dc039ff21d56a5bbdece793055c639e4.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 3dea86d2..1f798042 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1677499066-2cc4fc4384d64306a69d333d4d2587065c67c858.profdata
+chrome-win64-main-1677520818-54a8caacd34207e830f91c8bb4f314979b3698a9.profdata
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index f30505f..22fa430 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -371,17 +371,18 @@
   ui::AXTreeID previous_active_tree_id = model_.active_tree_id();
   model_.SetActiveTreeId(tree_id);
   model_.SetActiveUkmSourceId(ukm_source_id);
-  // Unserialize all pending updates on the formerly active AXTree.
+  // Delete all pending updates on the formerly active AXTree.
   // TODO(crbug.com/1266555): If distillation is in progress, cancel the
   // distillation request.
 #if DCHECK_IS_ON()
   DCHECK(pending_updates_.empty() ||
          model_.pending_updates_bundle_id() == previous_active_tree_id);
 #endif
-  UnserializeUpdates(std::move(pending_updates_), previous_active_tree_id);
+  pending_updates_.clear();
 #if DCHECK_IS_ON()
   model_.SetPendingUpdatesBundleId(ui::AXTreeIDUnknown());
 #endif
+
   // When the UI first constructs, this function may be called before tree_id
   // has been added to trees_ in AccessibilityEventReceived. In that case, do
   // not distill.
@@ -389,6 +390,7 @@
       base::Contains(trees_, model_.active_tree_id())) {
     Distill();
   }
+  OnAXTreeDestroyed(previous_active_tree_id);
 }
 
 void ReadAnythingAppController::OnAXTreeDestroyed(const ui::AXTreeID& tree_id) {
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index 1a655626..0ba1cda4 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -213,6 +213,10 @@
 
   size_t GetNumTrees() { return controller_->trees_.size(); }
 
+  bool HasTree(ui::AXTreeID tree_id) {
+    return base::Contains(controller_->trees_, tree_id);
+  }
+
   size_t GetNumPendingUpdates() { return controller_->pending_updates_.size(); }
 
   ui::AXTreeID tree_id_;
@@ -614,15 +618,11 @@
     updates.push_back(update);
   }
   // Add the three updates separately since they have different tree IDs.
-  for (int i = 0; i < 3; i++) {
-    AccessibilityEventReceived({updates[i]});
-  }
-
-  OnAXTreeDistilled({1});
-
   // Check that changing the active tree ID changes the active tree which is
   // used when using a v8 getter.
   for (int i = 0; i < 3; i++) {
+    AccessibilityEventReceived({updates[i]});
+    OnAXTreeDistilled({1});
     EXPECT_CALL(*distiller_, Distill).Times(1);
     OnActiveAXTreeIDChanged(tree_ids[i]);
     EXPECT_EQ("Tree " + base::NumberToString(i), GetTextContent(1));
@@ -633,6 +633,44 @@
   OnActiveAXTreeIDChanged(tree_ids[2]);
 }
 
+TEST_F(ReadAnythingAppControllerTest,
+       OnActiveAXTreeIDChanged_PreviousActiveTreeDestroyed) {
+  ui::AXTreeID tree_id_2 = ui::AXTreeID::CreateNewAXTreeID();
+  ui::AXTreeUpdate update_2;
+  SetUpdateTreeID(&update_2, tree_id_2);
+  update_2.root_id = 1;
+  update_2.nodes.resize(1);
+  update_2.nodes[0].id = 1;
+
+  ASSERT_EQ(1u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+
+  // Add update 2.
+  AccessibilityEventReceived({update_2});
+  ASSERT_EQ(2u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+  ASSERT_TRUE(HasTree(tree_id_2));
+  OnAXTreeDistilled({1});
+  EXPECT_CALL(*distiller_, Distill).Times(1);
+
+  // Change the active tree id to the same id. Nothing happens.
+  OnActiveAXTreeIDChanged(tree_id_);
+  ASSERT_EQ(2u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+  ASSERT_TRUE(HasTree(tree_id_2));
+
+  // Change the active tree id to tree id 2. The previously active tree id's
+  // tree is deleted.
+  OnActiveAXTreeIDChanged(tree_id_2);
+  ASSERT_EQ(1u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_2));
+
+  // Change the active tree id to the same id. The previously active tree id's
+  // tree is deleted.
+  OnActiveAXTreeIDChanged(tree_id_);
+  ASSERT_EQ(0u, GetNumTrees());
+}
+
 TEST_F(ReadAnythingAppControllerTest, DoesNotCrashIfActiveAXTreeIDUnknown) {
   EXPECT_CALL(*distiller_, Distill).Times(0);
   ui::AXTreeID tree_id = ui::AXTreeIDUnknown();
@@ -663,18 +701,27 @@
 
   // Start with 1 tree (the tree created in SetUp).
   ASSERT_EQ(1u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
 
   // Add the two trees.
   AccessibilityEventReceived({updates[0]});
   ASSERT_EQ(2u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+  ASSERT_TRUE(HasTree(tree_ids[0]));
   AccessibilityEventReceived({updates[1]});
   ASSERT_EQ(3u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+  ASSERT_TRUE(HasTree(tree_ids[0]));
+  ASSERT_TRUE(HasTree(tree_ids[1]));
 
   // Remove all of the trees.
   OnAXTreeDestroyed(tree_id_);
   ASSERT_EQ(2u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_ids[0]));
+  ASSERT_TRUE(HasTree(tree_ids[1]));
   OnAXTreeDestroyed(tree_ids[0]);
   ASSERT_EQ(1u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_ids[1]));
   OnAXTreeDestroyed(tree_ids[1]);
   ASSERT_EQ(0u, GetNumTrees());
 }
@@ -704,14 +751,21 @@
 
   // Start with 1 tree (the tree created in SetUp).
   ASSERT_EQ(1u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
   AccessibilityEventReceived({updates[0]});
   ASSERT_EQ(1u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
 
   // Add two trees.
   AccessibilityEventReceived({updates[1]});
   ASSERT_EQ(2u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+  ASSERT_TRUE(HasTree(tree_ids[0]));
   AccessibilityEventReceived({updates[2]});
   ASSERT_EQ(3u, GetNumTrees());
+  ASSERT_TRUE(HasTree(tree_id_));
+  ASSERT_TRUE(HasTree(tree_ids[0]));
+  ASSERT_TRUE(HasTree(tree_ids[1]));
 
   // Remove the grandparent tree (tree_ids[0]. When it is removed, its
   // child (tree_ids[1]) and its child's child (tree_ids[2]) are both removed.
@@ -840,15 +894,11 @@
   EXPECT_EQ(2u, GetNumPendingUpdates());
   EXPECT_EQ("5", GetTextContent(1));
 
-  // Switching the active AXTreeID flushes the pending updates.
+  // Switching the active AXTreeID deletes the pending updates.
   ui::AXTreeID tree_id_2 = ui::AXTreeID::CreateNewAXTreeID();
   EXPECT_CALL(*distiller_, Distill).Times(0);
   OnActiveAXTreeIDChanged(tree_id_2);
   EXPECT_EQ(0u, GetNumPendingUpdates());
-
-  EXPECT_CALL(*distiller_, Distill).Times(1);
-  OnActiveAXTreeIDChanged(tree_id_);
-  EXPECT_EQ("567", GetTextContent(1));
 }
 
 TEST_F(ReadAnythingAppControllerTest,
diff --git a/chrome/services/file_util/public/cpp/zip_file_creator_browsertest.cc b/chrome/services/file_util/public/cpp/zip_file_creator_browsertest.cc
index 5343f69..3eb757bf 100644
--- a/chrome/services/file_util/public/cpp/zip_file_creator_browsertest.cc
+++ b/chrome/services/file_util/public/cpp/zip_file_creator_browsertest.cc
@@ -26,11 +26,6 @@
 
 namespace {
 
-bool CreateFile(const base::FilePath& file, const std::string& content) {
-  return base::WriteFile(file, content.c_str(), content.size()) ==
-         static_cast<int>(content.size());
-}
-
 class ZipFileCreatorTest : public InProcessBrowserTest {
  protected:
   void SetUpOnMainThread() override {
@@ -276,7 +271,7 @@
     for (int i = 1; i < 90; i++) {
       base::FilePath file(std::to_string(i) + ".txt");
       std::string content = "Hello" + std::to_string(i);
-      ASSERT_TRUE(CreateFile(root_dir.Append(file), content));
+      ASSERT_TRUE(base::WriteFile(root_dir.Append(file), content));
       file_tree_content[file] = content;
     }
     for (int i = 1; i <= 10; i++) {
@@ -287,7 +282,7 @@
         base::FilePath file = dir.Append(std::to_string(j) + ".txt");
         std::string content =
             "Hello" + std::to_string(i) + "/" + std::to_string(j);
-        ASSERT_TRUE(CreateFile(root_dir.Append(file), content));
+        ASSERT_TRUE(base::WriteFile(root_dir.Append(file), content));
         file_tree_content[file] = content;
       }
     }
diff --git a/chrome/services/media_gallery_util/media_parser_android_unittest.cc b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
index a50d4537..eb75231 100644
--- a/chrome/services/media_gallery_util/media_parser_android_unittest.cc
+++ b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
@@ -179,7 +179,7 @@
 // Test to verify frame extraction will fail on invalid video file.
 TEST_F(MediaParserAndroidTest, VideoFrameExtractionInvalidFile) {
   base::FilePath dummy_file = temp_dir().AppendASCII("test.txt");
-  EXPECT_GT(base::WriteFile(dummy_file, "123", sizeof("123")), 0);
+  EXPECT_TRUE(base::WriteFile(dummy_file, "123"));
 
   EXPECT_FALSE(ExtractFrame(dummy_file, "video/webm"));
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 80b00069..603d7dbd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3553,7 +3553,9 @@
         "../browser/ui/views/web_apps/web_app_uninstall_dialog_browsertest.cc",
       ]
       if (is_win) {
-        sources += [ "../browser/ui/views/frame/glass_browser_frame_view_browsertest_win.cc" ]
+        sources += [
+          "../browser/ui/views/frame/browser_frame_view_browsertest_win.cc",
+        ]
       }
       if (is_mac) {
         sources += [
@@ -4025,6 +4027,7 @@
         "../browser/ash/policy/reporting/metrics_reporting/network/network_events_observer_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/usb/usb_events_browsertest.cc",
+        "../browser/ash/policy/reporting/os_updates/os_updates_reporter_browsertest.cc",
         "../browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter_browsertest.cc",
         "../browser/ash/policy/status_collector/child_status_collector_browsertest.cc",
         "../browser/ash/policy/status_collector/device_status_collector_browsertest.cc",
@@ -4843,6 +4846,7 @@
       "../browser/lacros/field_trial_service_lacros_browsertest.cc",
       "../browser/lacros/file_manager_lacros_browsertest.cc",
       "../browser/lacros/idle_service_lacros_browsertest.cc",
+      "../browser/lacros/multitask_menu_nudge_delegate_lacros_browsertest.cc",
       "../browser/sync/test/lacros/sync_apps_toggle_sharing_lacros_browsertest.cc",
       "../browser/sync/test/lacros/sync_custom_passphrase_sharing_lacros_browsertest.cc",
       "../browser/upgrade_detector/get_installed_version_lacros_browsertest.cc",
@@ -7497,6 +7501,7 @@
       "../browser/ui/ash/desks/chrome_desks_templates_delegate_unittest.cc",
       "../browser/ui/ash/device_scheduled_reboot/reboot_notification_controller_unittest.cc",
       "../browser/ui/ash/device_scheduled_reboot/scheduled_reboot_dialog_unittest.cc",
+      "../browser/ui/ash/glanceables/glanceables_keyed_service_factory_unittest.cc",
       "../browser/ui/ash/global_media_controls/media_notification_provider_impl_unittest.cc",
       "../browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc",
       "../browser/ui/ash/ime_controller_client_impl_unittest.cc",
diff --git a/chrome/test/base/devtools_listener.cc b/chrome/test/base/devtools_listener.cc
index aeea968..37f788a 100644
--- a/chrome/test/base/devtools_listener.cc
+++ b/chrome/test/base/devtools_listener.cc
@@ -177,7 +177,7 @@
 
   result->Set("result", std::move(entries));
   CHECK(base::JSONWriter::Write(*result, &coverage));
-  base::WriteFile(path, coverage.data(), coverage.size());
+  base::WriteFile(path, coverage);
 
   script_coverage_.clear();
   script_hash_map_.clear();
@@ -268,7 +268,7 @@
         store.AppendASCII("scripts").AppendASCII(hash.append(".js.json"));
     CHECK(base::JSONWriter::Write(*params, &text));
     if (!base::PathExists(path))  // script de-duplication
-      base::WriteFile(path, text.data(), text.size());
+      base::WriteFile(path, text);
     value_.clear();
   }
 }
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 2870b502..32b7bf4 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -289,8 +289,6 @@
     WindowOpenDisposition GetWindowOpenDisposition() const override;
     ui::PageTransition GetPageTransition() const override;
     base::TimeTicks GetMatchSelectionTimestamp() const override;
-    void AcceptInput() override {}
-    void AcceptInput(base::TimeTicks match_selection_timestamp) override {}
     void FocusLocation(bool select_all) override {}
     void FocusSearch() override {}
     void UpdateContentSettingsIcons() override {}
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc
index c5343ed4..f4d21430 100644
--- a/chrome/test/base/ui_test_utils.cc
+++ b/chrome/test/base/ui_test_utils.cc
@@ -456,11 +456,10 @@
 void SendToOmniboxAndSubmit(Browser* browser,
                             const std::string& input,
                             base::TimeTicks match_selection_timestamp) {
-  LocationBar* location_bar = browser->window()->GetLocationBar();
-  OmniboxView* omnibox = location_bar->GetOmniboxView();
+  OmniboxView* omnibox = browser->window()->GetLocationBar()->GetOmniboxView();
   omnibox->model()->OnSetFocus(/*control_down=*/false);
   omnibox->SetUserText(base::ASCIIToUTF16(input));
-  location_bar->AcceptInput(match_selection_timestamp);
+  omnibox->model()->OpenSelection(match_selection_timestamp);
 
   WaitForAutocompleteDone(browser);
 }
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index 44e8d5f..5a57d8b 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -939,8 +939,7 @@
   if (!temp_crx_dir.CreateUniqueTempDir())
     return Status(kUnknownError, "cannot create temp dir");
   base::FilePath extension_crx = temp_crx_dir.GetPath().AppendASCII("temp.crx");
-  int size = static_cast<int>(decoded_extension.length());
-  if (base::WriteFile(extension_crx, decoded_extension.c_str(), size) != size) {
+  if (!base::WriteFile(extension_crx, decoded_extension)) {
     return Status(kUnknownError, "cannot write file");
   }
 
@@ -1025,9 +1024,7 @@
   } else {
     manifest->Set("key", public_key_base64);
     base::JSONWriter::Write(*manifest, &manifest_data);
-    if (base::WriteFile(
-            manifest_path, manifest_data.c_str(), manifest_data.size()) !=
-        static_cast<int>(manifest_data.size())) {
+    if (!base::WriteFile(manifest_path, manifest_data)) {
       return Status(kUnknownError, "cannot add 'key' to manifest");
     }
   }
@@ -1107,8 +1104,7 @@
   base::JSONWriter::Write(*prefs, &prefs_str);
   VLOG(0) << "Populating " << path.BaseName().value()
           << " file: " << PrettyPrintValue(base::Value(prefs->Clone()));
-  if (static_cast<int>(prefs_str.length()) != base::WriteFile(
-          path, prefs_str.c_str(), prefs_str.length())) {
+  if (!base::WriteFile(path, prefs_str)) {
     return Status(kUnknownError, "failed to write prefs file");
   }
   return Status(kOk);
@@ -1155,8 +1151,7 @@
 
   // Write empty "First Run" file, otherwise Chrome will wipe the default
   // profile that was written.
-  if (base::WriteFile(
-          user_data_dir.Append(chrome::kFirstRunSentinel), "", 0) != 0) {
+  if (!base::WriteFile(user_data_dir.Append(chrome::kFirstRunSentinel), "")) {
     return Status(kUnknownError, "failed to write first run file");
   }
   return Status(kOk);
diff --git a/chrome/test/chromedriver/chrome_launcher_unittest.cc b/chrome/test/chromedriver/chrome_launcher_unittest.cc
index db86710e..49bfcdd 100644
--- a/chrome/test/chromedriver/chrome_launcher_unittest.cc
+++ b/chrome/test/chromedriver/chrome_launcher_unittest.cc
@@ -225,7 +225,7 @@
   char data[] = "12345\nblahblah";
   base::FilePath temp_file =
       temp_dir.GetPath().Append(FILE_PATH_LITERAL("DevToolsActivePort"));
-  ASSERT_TRUE(base::WriteFile(temp_file, data, strlen(data)));
+  ASSERT_TRUE(base::WriteFile(temp_file, data));
   int port;
   ASSERT_TRUE(
       internal::ParseDevToolsActivePortFile(temp_dir.GetPath(), &port).IsOk());
@@ -238,7 +238,7 @@
   char data[] = "12345";
   base::FilePath temp_file =
       temp_dir.GetPath().Append(FILE_PATH_LITERAL("DevToolsActivePort"));
-  ASSERT_TRUE(base::WriteFile(temp_file, data, strlen(data)));
+  ASSERT_TRUE(base::WriteFile(temp_file, data));
   int port = 1111;
   ASSERT_FALSE(
       internal::ParseDevToolsActivePortFile(temp_dir.GetPath(), &port).IsOk());
@@ -251,7 +251,7 @@
   char data[] = "12345asdf\nblahblah";
   base::FilePath temp_file =
       temp_dir.GetPath().Append(FILE_PATH_LITERAL("DevToolsActivePort"));
-  ASSERT_TRUE(base::WriteFile(temp_file, data, strlen(data)));
+  ASSERT_TRUE(base::WriteFile(temp_file, data));
   int port;
   ASSERT_FALSE(
       internal::ParseDevToolsActivePortFile(temp_dir.GetPath(), &port).IsOk());
@@ -274,7 +274,7 @@
   base::FilePath temp_file =
       temp_dir.GetPath().Append(FILE_PATH_LITERAL("DevToolsActivePort"));
   char data[] = "12345asdf\nblahblah";
-  base::WriteFile(temp_file, data, strlen(data));
+  base::WriteFile(temp_file, data);
   ASSERT_TRUE(
       internal::RemoveOldDevToolsActivePortFile(temp_dir.GetPath()).IsOk());
   ASSERT_FALSE(base::PathExists(temp_file));
diff --git a/chrome/test/chromedriver/util.cc b/chrome/test/chromedriver/util.cc
index 08ec8233..84638c2 100644
--- a/chrome/test/chromedriver/util.cc
+++ b/chrome/test/chromedriver/util.cc
@@ -104,9 +104,9 @@
     return Status(kUnknownError, "unable to create temp dir");
 
   base::FilePath archive = dir.GetPath().AppendASCII("temp.zip");
-  int length = bytes.length();
-  if (base::WriteFile(archive, bytes.c_str(), length) != length)
+  if (!base::WriteFile(archive, bytes)) {
     return Status(kUnknownError, "could not write file to temp dir");
+  }
 
   if (!zip::Unzip(archive, unzip_dir))
     return Status(kUnknownError, "could not unzip archive");
diff --git a/chrome/test/data/popup_blocker/popup-many-10.html b/chrome/test/data/popup_blocker/popup-many-10.html
index c93d380..6efb0d2 100644
--- a/chrome/test/data/popup_blocker/popup-many-10.html
+++ b/chrome/test/data/popup_blocker/popup-many-10.html
@@ -3,8 +3,8 @@
 <title>Popup created using window.open</title>
 <script>
 function popup(name) {
-  window.open("popup-success" + name + ".html",
-  name, "menubar=1, resizable=1, width=350, height=250");
+  window.open("popup-success.html", name,
+              "menubar=1, resizable=1, width=350, height=250");
 }
 
 function test() {
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
index 265905fc..4bb12376 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
@@ -4,9 +4,9 @@
 
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
-import {Cluster, RawVisitData, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import {Cluster, RawVisitData} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
 import {PageHandlerRemote} from 'chrome://new-tab-page/history_clusters.mojom-webui.js';
-import {historyClustersDescriptor, HistoryClustersModuleElement, HistoryClustersProxyImpl} from 'chrome://new-tab-page/lazy_load.js';
+import {HistoryClusterLayoutType, historyClustersDescriptor, HistoryClustersModuleElement, HistoryClustersProxyImpl} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
@@ -25,32 +25,18 @@
             new HistoryClustersProxyImpl(mock)));
   });
 
-  function createSampleCluster(overrides?: Partial<Cluster>): Cluster {
+  function createSampleCluster(
+      numVisits: number = 1, numImageVisits: number = 0,
+      overrides?: Partial<Cluster>): Cluster {
     const rawVisitData: RawVisitData = {
       url: {url: ''},
       visitTime: {internalValue: BigInt(0)},
     };
 
-    const urlVisit1: URLVisit = {
-      visitId: BigInt(1),
-      normalizedUrl: {url: 'https://www.google.com'},
-      urlForDisplay: 'https://www.google.com',
-      pageTitle: 'Test Title',
-      titleMatchPositions: [],
-      urlForDisplayMatchPositions: [],
-      duplicates: [],
-      relativeDate: '',
-      annotations: [],
-      debugInfo: {},
-      rawVisitData: rawVisitData,
-      imageUrl: undefined,
-      isKnownToSync: false,
-    };
-
     const cluster: Cluster = Object.assign(
         {
           id: BigInt(111),
-          visits: [urlVisit1],
+          visits: [],
           label: undefined,
           labelMatchPositions: [],
           relatedSearches: [],
@@ -60,6 +46,26 @@
         },
         overrides);
 
+    for (let i = 0; i < numVisits; i++) {
+      cluster.visits.push({
+        visitId: BigInt(i),
+        normalizedUrl: {url: `https://www.google.com/${i}`},
+        urlForDisplay: `www.google.com/${i}`,
+        pageTitle: `Test Title ${i}`,
+        titleMatchPositions: [],
+        urlForDisplayMatchPositions: [],
+        duplicates: [],
+        relativeDate: '',
+        annotations: [],
+        debugInfo: {},
+        rawVisitData: rawVisitData,
+        imageUrl: i >= numImageVisits ? undefined : {
+          url: `https://image.com/${i}`,
+        },
+        isKnownToSync: false,
+      });
+    }
+
     return cluster;
   }
 
@@ -76,10 +82,10 @@
     assertEquals(null, moduleElement);
   });
 
-  test('Module created when history cluster data available', async () => {
+  test('No module created when data does not match layouts', async () => {
     // Arrange.
     handler.setResultFor(
-        'getCluster', Promise.resolve({cluster: createSampleCluster()}));
+        'getCluster', Promise.resolve({cluster: createSampleCluster(2, 0)}));
 
     // Act.
     const moduleElement = await historyClustersDescriptor.initialize(0) as
@@ -87,12 +93,72 @@
 
     // Assert.
     await handler.whenCalled('getCluster');
-    assertTrue(!!moduleElement);
+    assertEquals(null, moduleElement);
+  });
+
+  test('Layout 1 is used', async () => {
+    // Arrange.
+    // 3 total visits (2 + SRP) with 2 being image visits.
+    handler.setResultFor(
+        'getCluster', Promise.resolve({cluster: createSampleCluster(3, 2)}));
+
+    // Act.
+    const moduleElement = await historyClustersDescriptor.initialize(0) as
+        HistoryClustersModuleElement;
+    document.body.append(moduleElement);
+    await waitAfterNextRender(moduleElement);
+
+    // Assert.
+    const layoutElements =
+        moduleElement.shadowRoot!.querySelectorAll('.layout');
+    assertEquals(HistoryClusterLayoutType.LAYOUT_1, moduleElement.layoutType);
+    assertEquals(layoutElements.length, 1);
+    assertEquals(layoutElements[0]!.id, 'layout1');
+  });
+
+  test('Layout 2 is used', async () => {
+    // Arrange.
+    // 4 total visits (3 + SRP) with 1 being an image visit.
+    handler.setResultFor(
+        'getCluster', Promise.resolve({cluster: createSampleCluster(4, 1)}));
+
+    // Act.
+    const moduleElement = await historyClustersDescriptor.initialize(0) as
+        HistoryClustersModuleElement;
+    document.body.append(moduleElement);
+    await waitAfterNextRender(moduleElement);
+
+    // Assert.
+    const layoutElements =
+        moduleElement.shadowRoot!.querySelectorAll('.layout');
+    assertEquals(HistoryClusterLayoutType.LAYOUT_2, moduleElement.layoutType);
+    assertEquals(layoutElements.length, 1);
+    assertEquals(layoutElements[0]!.id, 'layout2');
+  });
+
+  test('Layout 3 is used', async () => {
+    // Arrange.
+    // 5 total visits (4 + SRP) with 2 being image visits.
+    handler.setResultFor(
+        'getCluster', Promise.resolve({cluster: createSampleCluster(5, 2)}));
+
+    // Act.
+    const moduleElement = await historyClustersDescriptor.initialize(0) as
+        HistoryClustersModuleElement;
+    document.body.append(moduleElement);
+    await waitAfterNextRender(moduleElement);
+
+    // Assert.
+    const layoutElements =
+        moduleElement.shadowRoot!.querySelectorAll('.layout');
+    assertEquals(HistoryClusterLayoutType.LAYOUT_3, moduleElement.layoutType);
+    assertEquals(layoutElements.length, 1);
+    assertEquals(layoutElements[0]!.id, 'layout3');
   });
 
   test('Tile element populated with correct data', async () => {
     handler.setResultFor(
-        'getCluster', Promise.resolve({cluster: createSampleCluster()}));
+        'getCluster', Promise.resolve({cluster: createSampleCluster(3, 2)}));
 
     const moduleElement = await historyClustersDescriptor.initialize(0) as
         HistoryClustersModuleElement;
@@ -105,6 +171,6 @@
     const tileElement = $$(moduleElement, 'ntp-history-clusters-tile');
     assertTrue(!!tileElement);
 
-    assertEquals($$(tileElement, '#title')!.innerHTML, 'Test Title');
+    assertEquals($$(tileElement, '#title')!.innerHTML, 'Test Title 0');
   });
 });
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.ts b/chrome/test/data/webui/tab_strip/tab_list_test.ts
index fa79c61..02c44fb5 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.ts
@@ -306,6 +306,7 @@
 
   test('PlacePinnedTabElementAnimatesTabsWithinSameColumn', async () => {
     tabs.forEach(pinTabAt);
+    await flushTasks();
     await tabList.animationPromises;
 
     // Test moving a tab within the same column. If a tab is moved from index 0
diff --git a/chrome/test/data/webui/tab_strip/tab_test.ts b/chrome/test/data/webui/tab_strip/tab_test.ts
index ec7f471..cd0b90b5 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_test.ts
@@ -59,65 +59,6 @@
     document.body.appendChild(tabElement);
   });
 
-  test('slideIn animates scale for the last tab', async () => {
-    document.documentElement.dir = 'ltr';
-    tabElement.style.paddingRight = '100px';
-    const tabElementStyle = window.getComputedStyle(tabElement);
-
-    const animationPromise = tabElement.slideIn();
-    // Before animation completes.
-    assertEquals('20px', tabElementStyle.paddingRight);
-    assertEquals('280px', tabElementStyle.maxWidth);
-    assertEquals('matrix(0, 0, 0, 0, 0, 0)', tabElementStyle.transform);
-    await animationPromise;
-    // After animation completes.
-    assertEquals('100px', tabElementStyle.paddingRight);
-    assertEquals('none', tabElementStyle.maxWidth);
-    assertEquals('none', tabElementStyle.transform);
-  });
-
-  test('slideIn animations for not the last tab', async () => {
-    // Add another element to make sure the element being tested is not the
-    // last.
-    document.body.appendChild(document.createElement('div'));
-
-    document.documentElement.dir = 'ltr';
-    tabElement.style.paddingRight = '100px';
-    const tabElementStyle = window.getComputedStyle(tabElement);
-
-    const animationPromise = tabElement.slideIn();
-    // Before animation completes.
-    assertEquals('0px', tabElementStyle.paddingRight);
-    assertEquals('0px', tabElementStyle.maxWidth);
-    assertEquals('matrix(0, 0, 0, 0, 0, 0)', tabElementStyle.transform);
-    await animationPromise;
-    // After animation completes.
-    assertEquals('100px', tabElementStyle.paddingRight);
-    assertEquals('none', tabElementStyle.maxWidth);
-    assertEquals('none', tabElementStyle.transform);
-  });
-
-  test('slideIn animations right to left for RTL languages', async () => {
-    // Add another element to make sure the element being tested is not the
-    // last.
-    document.body.appendChild(document.createElement('div'));
-
-    document.documentElement.dir = 'rtl';
-    tabElement.style.paddingLeft = '100px';
-    const tabElementStyle = window.getComputedStyle(tabElement);
-
-    const animationPromise = tabElement.slideIn();
-    // Before animation completes.
-    assertEquals('0px', tabElementStyle.paddingLeft);
-    assertEquals('0px', tabElementStyle.maxWidth);
-    assertEquals('matrix(0, 0, 0, 0, 0, 0)', tabElementStyle.transform);
-    await animationPromise;
-    // After animation completes.
-    assertEquals('100px', tabElementStyle.paddingLeft);
-    assertEquals('none', tabElementStyle.maxWidth);
-    assertEquals('none', tabElementStyle.transform);
-  });
-
   test('slideOut animates out the element', async () => {
     testTabsApiProxy.setVisible(true);
     const tabElementStyle = window.getComputedStyle(tabElement);
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index fb52f2f2..aea70a3 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -1585,20 +1585,18 @@
   RUN_FILEIO_PRIVATE_SUBTESTS;
 }
 
-#define SETUP_FOR_FILEREF_TESTS                                              \
-  const char kContents[] = "Hello from browser";                             \
-  base::ScopedAllowBlockingForTesting allow_blocking;                        \
-  base::ScopedTempDir temp_dir;                                              \
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());                               \
-  base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo");  \
-  ASSERT_EQ(                                                                 \
-      static_cast<int>(sizeof(kContents) - 1),                               \
-      base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1)); \
-  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;     \
-  file_info_list.push_back(                                                  \
-      ui::SelectedFileInfo(existing_filename, existing_filename));           \
-  PPAPITestSelectFileDialogFactory test_dialog_factory(                      \
-      PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST,              \
+#define SETUP_FOR_FILEREF_TESTS                                             \
+  const char kContents[] = "Hello from browser";                            \
+  base::ScopedAllowBlockingForTesting allow_blocking;                       \
+  base::ScopedTempDir temp_dir;                                             \
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());                              \
+  base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo"); \
+  ASSERT_TRUE(base::WriteFile(existing_filename, kContents));               \
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;    \
+  file_info_list.emplace_back(                                              \
+      ui::SelectedFileInfo(existing_filename, existing_filename));          \
+  PPAPITestSelectFileDialogFactory test_dialog_factory(                     \
+      PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST,             \
       file_info_list);
 
 // FileRef tests.
diff --git a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
index 30e9e0e1..f141cfd 100644
--- a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
@@ -149,9 +149,7 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo");
-  ASSERT_EQ(
-      static_cast<int>(sizeof(kContents) - 1),
-      base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1));
+  ASSERT_TRUE(base::WriteFile(existing_filename, kContents));
 
   PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
@@ -334,9 +332,7 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo");
-  ASSERT_EQ(
-      static_cast<int>(sizeof(kContents) - 1),
-      base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1));
+  ASSERT_TRUE(base::WriteFile(existing_filename, kContents));
 
   safe_browsing_test_configuration_.default_result =
       safe_browsing::DownloadCheckResult::DANGEROUS;
diff --git a/chromecast/base/cast_sys_info_dummy.cc b/chromecast/base/cast_sys_info_dummy.cc
index 1b9f917..feee2d31 100644
--- a/chromecast/base/cast_sys_info_dummy.cc
+++ b/chromecast/base/cast_sys_info_dummy.cc
@@ -27,13 +27,12 @@
                            const std::string& default_val) {
   DCHECK(sys_info_file.is_dict());
 
-  const base::Value* val =
-      sys_info_file.FindKeyOfType(key, base::Value::Type::STRING);
+  const std::string* val = sys_info_file.GetDict().FindString(key);
   if (!val) {
     LOG(WARNING) << "Json key not found: " << key;
     return default_val;
   }
-  return val->GetString();
+  return *val;
 }
 }  // namespace
 
diff --git a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
index 44d1feb..bf81f6e 100644
--- a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
+++ b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
@@ -33,12 +33,11 @@
                    base::Value::List& prerender_pipeline,
                    base::Value::List& postrender_pipeline) {
   bool has_render = false;
-  for (const base::Value& processor_description_dict : processors_list) {
-    DCHECK(processor_description_dict.is_dict());
-    std::string processor_name;
-    const base::Value* name_val = processor_description_dict.FindKeyOfType(
-        kNameKey, base::Value::Type::STRING);
-    if (name_val && name_val->GetString() == kRenderNameTag) {
+  for (const base::Value& processor_description_value : processors_list) {
+    DCHECK(processor_description_value.is_dict());
+    const std::string* name =
+        processor_description_value.GetDict().FindString(kNameKey);
+    if (name && *name == kRenderNameTag) {
       has_render = true;
       break;
     }
@@ -47,9 +46,9 @@
   bool is_prerender = has_render;
 
   for (const base::Value& processor_description_dict : processors_list) {
-    const base::Value* name_val = processor_description_dict.FindKeyOfType(
-        kNameKey, base::Value::Type::STRING);
-    if (name_val && name_val->GetString() == kRenderNameTag) {
+    const std::string* name =
+        processor_description_dict.GetDict().FindString(kNameKey);
+    if (name && *name == kRenderNameTag) {
       is_prerender = false;
       continue;
     }
@@ -172,9 +171,9 @@
 
 StreamPipelineDescriptor PostProcessingPipelineParser::GetPipelineByKey(
     const std::string& key) {
-  const base::Value* stream_dict =
+  const base::Value* stream_value =
       postprocessor_config_ ? postprocessor_config_->FindPath(key) : nullptr;
-  if (!postprocessor_config_ || !stream_dict) {
+  if (!postprocessor_config_ || !stream_value) {
     LOG(WARNING) << "No post-processor description found for \"" << key
                  << "\" in " << file_path_ << ". Using passthrough.";
     return StreamPipelineDescriptor(base::Value(base::Value::Type::LIST),
@@ -182,24 +181,29 @@
                                     nullptr, absl::nullopt, nullptr);
   }
 
+  const base::Value::Dict& stream_dict = stream_value->GetDict();
   const base::Value::List* processors_list =
-      stream_dict->GetDict().FindList(kProcessorsKey);
+      stream_dict.FindList(kProcessorsKey);
   CHECK(processors_list);
 
   base::Value::List prerender_pipeline;
   base::Value::List postrender_pipeline;
   SplitPipeline(*processors_list, prerender_pipeline, postrender_pipeline);
 
-  const base::Value* streams_list =
-      stream_dict->FindKeyOfType(kStreamsKey, base::Value::Type::LIST);
+  const base::Value* streams_list = stream_dict.Find(kStreamsKey);
+  if (streams_list && !streams_list->is_list()) {
+    streams_list = nullptr;
+  }
 
-  const base::Value* volume_limits =
-      stream_dict->FindKeyOfType(kVolumeLimitsKey, base::Value::Type::DICT);
+  const base::Value* volume_limits = stream_dict.Find(kVolumeLimitsKey);
+  if (volume_limits && !volume_limits->is_dict()) {
+    volume_limits = nullptr;
+  }
 
   return StreamPipelineDescriptor(
       base::Value(std::move(prerender_pipeline)),
       base::Value(std::move(postrender_pipeline)), streams_list,
-      stream_dict->FindIntKey(kNumInputChannelsKey), volume_limits);
+      stream_dict.FindInt(kNumInputChannelsKey), volume_limits);
 }
 
 base::FilePath PostProcessingPipelineParser::GetFilePath() const {
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl.cc b/chromeos/ash/components/audio/cros_audio_config_impl.cc
index 942edf6..4d03184 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl.cc
+++ b/chromeos/ash/components/audio/cros_audio_config_impl.cc
@@ -18,9 +18,9 @@
 
 // Histogram names.
 constexpr char kOutputMuteChangeHistogramName[] =
-    "ChromeOS.Settings.Device.Audio.OutputMuteStateChange";
+    "ChromeOS.CrosAudioConfig.OutputMuteStateChange";
 constexpr char kInputMuteChangeHistogramName[] =
-    "ChromeOS.Settings.Device.Audio.InputMuteStateChange";
+    "ChromeOS.CrosAudioConfig.InputMuteStateChange";
 
 // Creates an inactive input device with default property configuration.
 AudioDevice CreateStubInternalMic() {
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc b/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
index 5ec24e88..662f6c40 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
+++ b/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
@@ -43,9 +43,9 @@
 
 // Histogram names.
 constexpr char kOutputMuteChangeHistogramName[] =
-    "ChromeOS.Settings.Device.Audio.OutputMuteStateChange";
+    "ChromeOS.CrosAudioConfig.OutputMuteStateChange";
 constexpr char kInputMuteChangeHistogramName[] =
-    "ChromeOS.Settings.Device.Audio.InputMuteStateChange";
+    "ChromeOS.CrosAudioConfig.InputMuteStateChange";
 
 struct AudioNodeInfo {
   bool is_input;
diff --git a/chromeos/ash/services/recording/BUILD.gn b/chromeos/ash/services/recording/BUILD.gn
index 13a6edda..1fda135 100644
--- a/chromeos/ash/services/recording/BUILD.gn
+++ b/chromeos/ash/services/recording/BUILD.gn
@@ -8,8 +8,11 @@
 
 source_set("recording") {
   sources = [
+    "color_quantization.cc",
+    "color_quantization.h",
     "gif_encoder.cc",
     "gif_encoder.h",
+    "gif_encoding_types.h",
     "gif_file_writer.cc",
     "gif_file_writer.h",
     "lzw_pixel_color_indices_writer.cc",
diff --git a/chromeos/ash/services/recording/DEPS b/chromeos/ash/services/recording/DEPS
index 375f8a1a..30aec87 100644
--- a/chromeos/ash/services/recording/DEPS
+++ b/chromeos/ash/services/recording/DEPS
@@ -12,6 +12,8 @@
   "+services/viz/privileged/mojom/compositing",
   "+testing/gmock/include/gmock/gmock.h",
   "+testing/gtest/include/gtest/gtest.h",
+  "+third_party/skia/include/core/SkBitmap.h",
+  "+third_party/skia/include/core/SkColor.h",
   "+ui/gfx",
 
   # Abseil is allowed by default, but some features are banned. See
diff --git a/chromeos/ash/services/recording/color_quantization.cc b/chromeos/ash/services/recording/color_quantization.cc
new file mode 100644
index 0000000..26475ed
--- /dev/null
+++ b/chromeos/ash/services/recording/color_quantization.cc
@@ -0,0 +1,103 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/services/recording/color_quantization.h"
+
+#include <cstdint>
+
+#include "base/check_op.h"
+#include "chromeos/ash/services/recording/gif_encoding_types.h"
+
+namespace recording {
+
+namespace {
+
+// If no color exists in the color table whose squared distance to the current
+// color being added is smaller than this value, the new color will be added to
+// the table.
+constexpr uint32_t kMinSquaredDistanceToAddColor = 75;
+
+// Calculates and returns the squared value of the Euclidean distance between
+// the two given colors.
+uint32_t CalculateColorDistanceSquared(const SkColor& color_a,
+                                       const SkColor& color_b) {
+  const uint32_t diff_r = SkColorGetR(color_a) - SkColorGetR(color_b);
+  const uint32_t diff_g = SkColorGetG(color_a) - SkColorGetG(color_b);
+  const uint32_t diff_b = SkColorGetB(color_a) - SkColorGetB(color_b);
+  return diff_r * diff_r + diff_g * diff_g + diff_b * diff_b;
+}
+
+// If `new_color` already exists in `out_palette`, it returns its index
+// immediately. Otherwise, it tries to add it to the palette if possible (i.e.
+// if there's still room in the palette and there's no other color that is
+// considered close enough), and returns the index. If addition is not possible,
+// it returns the index of the closest color in the palette.
+ColorIndex MaybeAddColorToPalette(const SkColor& new_color,
+                                  ColorTable& out_palette) {
+  int index_of_closest = -1;
+  uint32_t min_squared_distance = std::numeric_limits<uint32_t>::max();
+  const size_t current_size = out_palette.size();
+  for (size_t i = 0; i < current_size; ++i) {
+    const auto& current_color = out_palette[i];
+    if (current_color == new_color) {
+      return i;
+    }
+
+    const uint32_t squared_distance =
+        CalculateColorDistanceSquared(new_color, current_color);
+    if (squared_distance < min_squared_distance) {
+      min_squared_distance = squared_distance;
+      index_of_closest = i;
+    }
+  }
+
+  if (current_size < kMaxNumberOfColorsInPalette &&
+      min_squared_distance >= kMinSquaredDistanceToAddColor) {
+    out_palette.push_back(new_color);
+    return current_size;
+  }
+
+  DCHECK_NE(index_of_closest, -1);
+  return index_of_closest;
+}
+
+}  // namespace
+
+// TODO(b/270604745): Implement a better color quantization algorithm.
+void BuildColorPaletteAndPixelIndices(const SkBitmap& bitmap,
+                                      ColorTable& out_color_palette,
+                                      ColorIndices& out_pixel_color_indices) {
+  out_color_palette.clear();
+  out_pixel_color_indices.clear();
+
+  for (int row = 0; row < bitmap.height(); ++row) {
+    for (int col = 0; col < bitmap.width(); ++col) {
+      // We do not care about the alpha values, since our palette contains only
+      // RGB colors, therefore we make the color as fully opaque before calling
+      // `MaybeAddColorToPalette()` to make color comparison with the `==`
+      // operator possible.
+      const auto color = SkColorSetA(bitmap.getColor(col, row), 0xFF);
+      const ColorIndex index = MaybeAddColorToPalette(color, out_color_palette);
+      DCHECK_GE(index, 0);
+      DCHECK_LT(index, out_color_palette.size());
+      out_pixel_color_indices.push_back(index);
+    }
+  }
+
+  DCHECK_LE(out_color_palette.size(), kMaxNumberOfColorsInPalette);
+}
+
+uint8_t CalculateColorBitDepth(const ColorTable& color_palette) {
+  DCHECK_LE(color_palette.size(), kMaxNumberOfColorsInPalette);
+
+  uint8_t bit_depth = 1;
+  while ((1u << bit_depth) < color_palette.size()) {
+    ++bit_depth;
+  }
+
+  DCHECK_LE(bit_depth, kMaxColorBitDepth);
+  return bit_depth;
+}
+
+}  // namespace recording
diff --git a/chromeos/ash/services/recording/color_quantization.h b/chromeos/ash/services/recording/color_quantization.h
new file mode 100644
index 0000000..9529e9f4
--- /dev/null
+++ b/chromeos/ash/services/recording/color_quantization.h
@@ -0,0 +1,34 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_SERVICES_RECORDING_COLOR_QUANTIZATION_H_
+#define CHROMEOS_ASH_SERVICES_RECORDING_COLOR_QUANTIZATION_H_
+
+#include "chromeos/ash/services/recording/gif_encoding_types.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace recording {
+
+// GIF images can have a maximum number of 256 colors in their color tables.
+// This means that the minimum number of bits needed to represent this count is
+// 8, which is the max bit depth value.
+constexpr size_t kMaxNumberOfColorsInPalette = 256;
+constexpr uint8_t kMaxColorBitDepth = 8;
+
+// Performs color quantization on the given `bitmap` and fills
+// `out_color_palette` with the most important 256 colors in the image, and also
+// fills `out_pixel_color_indices` with the indices of the chosen colors from
+// `out_color_palette` for all the pixels in `bitmap`.
+void BuildColorPaletteAndPixelIndices(const SkBitmap& bitmap,
+                                      ColorTable& out_color_palette,
+                                      ColorIndices& out_pixel_color_indices);
+
+// Calculates and returns the color bit depth based on the size of the given
+// `color_palette`. The color bit depth is the least number of bits needed to be
+// able to represent the size of the palette as a binary number.
+uint8_t CalculateColorBitDepth(const ColorTable& color_palette);
+
+}  // namespace recording
+
+#endif  // CHROMEOS_ASH_SERVICES_RECORDING_COLOR_QUANTIZATION_H_
diff --git a/chromeos/ash/services/recording/gif_encoder.cc b/chromeos/ash/services/recording/gif_encoder.cc
index d67de4bd..72a4286 100644
--- a/chromeos/ash/services/recording/gif_encoder.cc
+++ b/chromeos/ash/services/recording/gif_encoder.cc
@@ -4,14 +4,187 @@
 
 #include "chromeos/ash/services/recording/gif_encoder.h"
 
+#include <cmath>
+
 #include "base/notreached.h"
+#include "base/time/time.h"
+#include "chromeos/ash/services/recording/color_quantization.h"
 #include "chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h"
 #include "chromeos/ash/services/recording/recording_encoder.h"
 #include "media/base/audio_bus.h"
 #include "media/base/video_frame.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace recording {
 
+namespace {
+
+// The value of the first byte of any extension block, such as the Netscape
+// Extension, and the Graphic Control Extension.
+constexpr uint8_t kExtensionIntroducer = 0x21;
+
+// -----------------------------------------------------------------------------
+// GlobalColorTableFields:
+
+// The bit fields packed in a single byte, which define the configuration of
+// the Global Color Table as part of the Logical Screen Descriptor block of the
+// GIF file.
+union GlobalColorTableFields {
+  struct {
+    // The least significant 3 bits, which define the number of colors in the
+    // table (2 ^ (global_color_table_size + 1) colors). The value
+    // `global_color_table_size + 1` is known as the bit depth. For example, if
+    // `global_color_table_size` is 7 (i.e. 0b111), this means there are 2 ^ 8
+    // colors in the table, where 8 is the color bit depth.
+    // This value is meaningful only if `global_color_table_flag` is set to 1.
+    uint8_t global_color_table_size : 3;
+
+    // This bit indicates whether the global color table is sorted (0b1) or not
+    // (0b0).
+    // This value is meaningful only if `global_color_table_flag` is set to 1.
+    uint8_t sort_flag : 1;
+
+    // For our purposes, this value is always set to 7 (0b111). Its explanation
+    // is copied as is from the GIF specs below:
+    // "Number of bits per primary color available to the original image, minus
+    // 1. This value represents the size of the entire palette from which the
+    // colors in the graphic were selected, not the number of colors actually
+    // used in the graphic. It indicates the richness of the original palette".
+    uint8_t color_resolution : 3;
+
+    // This is the most significant bit, and it indicates whether a global color
+    // table will immediately follow the Logical Screen Descriptor block (0b1)
+    // or not (0b0).
+    uint8_t global_color_table_flag : 1;
+  };
+
+  // The packed value of the above bit fields as an 8-bit byte.
+  uint8_t value;
+};
+
+static_assert(sizeof(GlobalColorTableFields) == 1,
+              "Unexpected size of GlobalColorTableFields");
+
+// -----------------------------------------------------------------------------
+// GraphicControlExtensionFields:
+
+// The bit fields packed in a single byte, which define some settings that the
+// decoder can use while decoding and rendering the GIF file.
+union GraphicControlExtensionFields {
+  struct {
+    // Indicates whether a transparency color index is given as part of the
+    // Graphic Control Extension block.
+    uint8_t transparent_color_flag : 1;
+
+    // Indicates whether or not user input is expected before continuing. If the
+    // flag is set, processing will continue when user input is entered.
+    uint8_t user_input_flag : 1;
+
+    // Indicates the way in which the decoded image frame is to be treated after
+    // being displayed.
+    // 0b000: No disposal specified. The decoder is not required to take any
+    //        action.
+    // 0b001: Do not dispose. The image is to be left in place.
+    // 0b010: The area used by the image must be restored to the background
+    //        color.
+    // 0b011: Restore to previous. The decoder is required to restore the area
+    //        overwritten by the image with what was there prior to rendering
+    //        it.
+    // 0b100 - 0b111: Not defined.
+    uint8_t disposal_method : 3;
+
+    // Reserved for future use.
+    uint8_t reserved : 3;
+  };
+
+  // The packed value of the above bit fields as an 8-bit byte.
+  uint8_t value;
+};
+
+static_assert(sizeof(GraphicControlExtensionFields) == 1,
+              "Unexpected size of GraphicControlExtensionFields");
+
+// -----------------------------------------------------------------------------
+// ImageDescriptorFields:
+
+// The bit fields packed in a single byte, which define the configuration of
+// the Local Color Table as part of the Image Descriptor block of the GIF file.
+union ImageDescriptorFields {
+  struct {
+    // Similar to `GlobalColorTableFields::global_color_table_size`.
+    uint8_t local_color_table_size : 3;
+
+    // Reserved for future use.
+    uint8_t reserved : 2;
+
+    // Similar to `GlobalColorTableFields::sort_flag`.
+    uint8_t sort_flag : 1;
+
+    // This bit is always set to 0 for our purposes.
+    uint8_t interlace_flag : 1;
+
+    // Indicates whether or not a Local Color Table will immediately follow the
+    // Image Descriptor block in the GIF file.
+    uint8_t local_color_table_flag : 1;
+  };
+
+  // The packed value of the above bit fields as an 8-bit byte.
+  uint8_t value;
+};
+
+static_assert(sizeof(ImageDescriptorFields) == 1,
+              "Unexpected size of ImageDescriptorFields");
+
+// -----------------------------------------------------------------------------
+// GifEncoderCapabilities:
+
+// Implements the capabilities for GIF encoding.
+class GifEncoderCapabilities : public RecordingEncoder::Capabilities {
+ public:
+  GifEncoderCapabilities() = default;
+  GifEncoderCapabilities(const GifEncoderCapabilities&) = delete;
+  GifEncoderCapabilities& operator=(const GifEncoderCapabilities&) = delete;
+  ~GifEncoderCapabilities() override = default;
+
+  // RecordingEncoder::Capabilities:
+  media::VideoPixelFormat GetSupportedPixelFormat() const override {
+    return media::PIXEL_FORMAT_ARGB;
+  }
+
+  bool SupportsVideoFrameSizeChanges() const override {
+    // The GIF file specifies the dimensions of the canvas only once in
+    // `InitializeVideoEncoder()` when the Logical Screen Descriptor is written.
+    // As a result, we cannot change the dimensions of the image after the fact
+    // in the middle of recording.
+    // TODO(afakhry): Figure out if we can allow this if the dimensions change
+    // to something smaller than the initial size.
+    return false;
+  }
+};
+
+// -----------------------------------------------------------------------------
+
+// Wraps the given `video_frame` pixels in a bitmap and returns it. Note that
+// this does not copy the pixels bytes from `video_frame` to the returned
+// bitmap, and hence the bitmap is safe to access as long as the `video_frame`
+// is alive.
+SkBitmap WrapVideoFrameInBitmap(const media::VideoFrame& video_frame) {
+  const gfx::Size visible_size = video_frame.visible_rect().size();
+  const SkImageInfo image_info = SkImageInfo::MakeN32(
+      visible_size.width(), visible_size.height(), kPremul_SkAlphaType,
+      video_frame.ColorSpace().ToSkColorSpace());
+
+  SkBitmap bitmap;
+  const uint8_t* pixels =
+      video_frame.visible_data(media::VideoFrame::kARGBPlane);
+  bitmap.installPixels(
+      SkPixmap(image_info, pixels,
+               video_frame.row_bytes(media::VideoFrame::kARGBPlane)));
+  return bitmap;
+}
+
+}  // namespace
+
 // static
 base::SequenceBound<GifEncoder> GifEncoder::Create(
     scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
@@ -25,6 +198,12 @@
       std::move(on_failure_callback));
 }
 
+// static
+std::unique_ptr<RecordingEncoder::Capabilities>
+GifEncoder::CreateCapabilities() {
+  return std::make_unique<GifEncoderCapabilities>();
+}
+
 GifEncoder::GifEncoder(
     PassKey,
     const media::VideoEncoder::Options& video_encoder_options,
@@ -42,9 +221,47 @@
 GifEncoder::~GifEncoder() = default;
 
 void GifEncoder::InitializeVideoEncoder(
-    const media::VideoEncoder::Options& video_encoder_options) {}
+    const media::VideoEncoder::Options& video_encoder_options) {
+  // There can be a maximum of 256 colors in our color palette, and
+  // `width * height` pixels.
+  color_palette_.reserve(kMaxNumberOfColorsInPalette);
+  pixel_color_indices_.reserve(video_encoder_options.frame_size.GetArea());
 
-void GifEncoder::EncodeVideo(scoped_refptr<media::VideoFrame> frame) {}
+  gif_file_writer_.WriteString("GIF89a");  // The GIF header.
+  WriteLogicalScreenDescriptor(video_encoder_options.frame_size);
+  WriteNetscapeExtension();
+}
+
+void GifEncoder::EncodeVideo(scoped_refptr<media::VideoFrame> frame) {
+  // This bitmap is backed up by the same memory containing the bytes of the
+  // frame. `SkBitmap` makes it more convenient to extract the colors from the
+  // video frame. Once we extract the color palette and pixel color indices from
+  // the `bitmap`, we no longer need it, nor need the video `frame`.
+  const SkBitmap bitmap = WrapVideoFrameInBitmap(*frame);
+  BuildColorPaletteAndPixelIndices(bitmap, color_palette_,
+                                   pixel_color_indices_);
+
+  const gfx::Size visible_size = frame->visible_rect().size();
+  DCHECK_EQ(pixel_color_indices_.size(),
+            static_cast<size_t>(visible_size.GetArea()));
+  const auto frame_time =
+      frame->metadata().reference_time.value_or(base::TimeTicks::Now());
+
+  // We're done with the frame, release it immediately before we spend cycles
+  // doing the encoding and writing to the file. This returns it back to the
+  // video frame buffer pool in Viz, which has a maximum number of in-flight
+  // frames. Releasing the frame as soon as we're done with it helps to avoid
+  // reaching that limit often.
+  frame.reset();
+
+  WriteGraphicControlExtension(frame_time);
+  const auto color_bit_depth = CalculateColorBitDepth(color_palette_);
+  WriteImageDescriptor(visible_size, color_bit_depth);
+  WriteColorPalette(color_bit_depth);
+  lzw_encoder_.EncodeAndWrite(pixel_color_indices_, color_bit_depth);
+
+  last_frame_time_ = frame_time;
+}
 
 void GifEncoder::EncodeAudio(std::unique_ptr<media::AudioBus> audio_bus,
                              base::TimeTicks capture_time) {
@@ -52,7 +269,150 @@
 }
 
 void GifEncoder::FlushAndFinalize(base::OnceClosure on_done) {
+  // Write the character ';' (or `0x3B` in hex), which is the GIF trailer byte,
+  // and flush the contents of the file.
+  gif_file_writer_.WriteByte(0x3B);
+  gif_file_writer_.FlushFile();
+
   std::move(on_done).Run();
 }
 
+void GifEncoder::WriteLogicalScreenDescriptor(const gfx::Size& frame_size) {
+  gif_file_writer_.WriteShort(frame_size.width());
+  gif_file_writer_.WriteShort(frame_size.height());
+
+  // We will not use a global color table, so we will set its flag to 0. The
+  // palette will be written once for every frame in the local color table after
+  // the image descriptor.
+  gif_file_writer_.WriteByte(GlobalColorTableFields{
+      .global_color_table_size = 0b000,
+      .sort_flag = 0b0,
+      .color_resolution = 0b111,
+      .global_color_table_flag = 0b0,
+  }
+                                 .value);
+
+  gif_file_writer_.WriteByte(0x00);  // Background color index.
+  gif_file_writer_.WriteByte(0x00);  // Pixel aspect ratio.
+}
+
+void GifEncoder::WriteNetscapeExtension() {
+  gif_file_writer_.WriteByte(kExtensionIntroducer);
+  // Extension Label. Always set to 0xFF for application extensions, which the
+  // Netscape extension is an example of.
+  gif_file_writer_.WriteByte(0xFF);
+  // The size of the following block that contains the application identifier,
+  // and the authentication code ("NETSCAPE2.0" in our case; i.e. 11 bytes).
+  gif_file_writer_.WriteByte(11);
+  gif_file_writer_.WriteString("NETSCAPE2.0");
+
+  // Next are the application data sub-blocks. Each sub-block begins with a byte
+  // that indicates its size, followed by a byte for its ID, followed by the
+  // payload of the sub-block itself, and finally a block terminator byte which
+  // is always `0x00`.
+  // For the Netscape extension, we have only 1 sub-block that defines the
+  // config for the looping of the animated GIF image.
+  // The sub-block is 3 bytes (excluding the block terminator).
+  gif_file_writer_.WriteByte(3);
+  // The sub block ID is 1.
+  gif_file_writer_.WriteByte(1);
+  // GIF animation loop config as a 16-bit unsigned integer. The value `0` means
+  // keep looping forever.
+  gif_file_writer_.WriteShort(0);
+  // The block terminator.
+  gif_file_writer_.WriteByte(0);
+}
+
+void GifEncoder::WriteGraphicControlExtension(
+    base::TimeTicks current_frame_time) {
+  gif_file_writer_.WriteByte(kExtensionIntroducer);
+  // The Extension Label. Always set to 0xF9 for Graphic Control Extension.
+  gif_file_writer_.WriteByte(0xF9);
+  // The number of bytes that follow (excluding the block terminator).
+  gif_file_writer_.WriteByte(4);
+
+  // Write the packed fields, such that we specify that we won't provide a
+  // transparent color index, and user input is not expected to proceed to the
+  // next frame.
+  gif_file_writer_.WriteByte(GraphicControlExtensionFields{
+      .transparent_color_flag = 0b0,
+      .user_input_flag = 0b0,
+      .disposal_method = 0b000,
+      .reserved = 0b000,
+  }
+                                 .value);
+
+  // Write the delay after which this frame that is being encoded should be
+  // rendered. The delay is specified in units of 1/100 (hundredth) of a second.
+  if (last_frame_time_.is_null()) {
+    // If this is the first ever frame, then there should be no delay.
+    gif_file_writer_.WriteShort(0);
+  } else {
+    const uint16_t delay = std::floor(
+        (current_frame_time - last_frame_time_).InSecondsF() * 100.f);
+    gif_file_writer_.WriteShort(delay);
+  }
+
+  // We're not using a transparent color (see above), so the value we write here
+  // doesn't matter and will be ignored.
+  gif_file_writer_.WriteByte(0);
+
+  // The block terminator.
+  gif_file_writer_.WriteByte(0);
+}
+
+void GifEncoder::WriteImageDescriptor(const gfx::Size& frame_size,
+                                      uint8_t color_bit_depth) {
+  DCHECK_LE(color_bit_depth, kMaxColorBitDepth);
+
+  // The Image Separator byte. Always 0x2C.
+  gif_file_writer_.WriteByte(0x2C);
+  // The "left" (or X coordinate) of the frame within the canvas bounds (which
+  // was defined earlier in the Logical Screen Descriptor when the encoder was
+  // initialized).
+  gif_file_writer_.WriteShort(0);
+  // The "top" (or Y coordinate) of the frame.
+  gif_file_writer_.WriteShort(0);
+  // The frame size.
+  gif_file_writer_.WriteShort(frame_size.width());
+  gif_file_writer_.WriteShort(frame_size.height());
+
+  // Write the Image Descriptor bitfields such that we specify that we're using
+  // a non-sorted, non-interlaced local color table of size 2 ^ color_bit_depth
+  // colors.
+  gif_file_writer_.WriteByte(ImageDescriptorFields{
+      .local_color_table_size = static_cast<uint8_t>((color_bit_depth - 1)),
+      .reserved = 0b00,
+      .sort_flag = 0b0,
+      .interlace_flag = 0b0,
+      .local_color_table_flag = 0b1,
+  }
+                                 .value);
+}
+
+void GifEncoder::WriteColorPalette(uint8_t color_bit_depth) {
+  DCHECK_LE(color_bit_depth, kMaxColorBitDepth);
+
+  const size_t table_size = 1u << color_bit_depth;
+  const size_t end = std::min(table_size, color_palette_.size());
+  for (size_t i = 0; i < end; ++i) {
+    const auto& color = color_palette_[i];
+    gif_file_writer_.WriteByte(SkColorGetR(color));
+    gif_file_writer_.WriteByte(SkColorGetG(color));
+    gif_file_writer_.WriteByte(SkColorGetB(color));
+  }
+
+  // The color table size that we write to the GIF file has to be a multiple of
+  // 2 (2 ^ color_bit_depth). See
+  // `ImageDescriptorFields::local_color_table_size`.
+  // However, we may have less colors in our palette that we extracted from the
+  // current video frame than a value that is a multiple of 2. We fill the
+  // remaining colors in the table as zeros.
+  for (size_t i = end; i < table_size; ++i) {
+    gif_file_writer_.WriteByte(0);
+    gif_file_writer_.WriteByte(0);
+    gif_file_writer_.WriteByte(0);
+  }
+}
+
 }  // namespace recording
diff --git a/chromeos/ash/services/recording/gif_encoder.h b/chromeos/ash/services/recording/gif_encoder.h
index 01412c7..690b551 100644
--- a/chromeos/ash/services/recording/gif_encoder.h
+++ b/chromeos/ash/services/recording/gif_encoder.h
@@ -5,8 +5,12 @@
 #ifndef CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODER_H_
 #define CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODER_H_
 
+#include <vector>
+
 #include "base/threading/sequence_bound.h"
+#include "base/time/time.h"
 #include "base/types/pass_key.h"
+#include "chromeos/ash/services/recording/gif_encoding_types.h"
 #include "chromeos/ash/services/recording/gif_file_writer.h"
 #include "chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h"
 #include "chromeos/ash/services/recording/recording_encoder.h"
@@ -48,6 +52,10 @@
       const base::FilePath& gif_file_path,
       OnFailureCallback on_failure_callback);
 
+  // Creates and returns an object that specifies the capabilities of this
+  // encoder.
+  static std::unique_ptr<Capabilities> CreateCapabilities();
+
   GifEncoder(
       PassKey,
       const media::VideoEncoder::Options& video_encoder_options,
@@ -67,6 +75,54 @@
   void FlushAndFinalize(base::OnceClosure on_done) override;
 
  private:
+  // Writes the Logical Screen Descriptor block to the GIF file. This block
+  // contains info about the dimensions of the canvas within which the images of
+  // the frames will be rendered. It also contains the configuration of the
+  // Global Color Table.
+  // This block is written only once in the entire GIF file.
+  void WriteLogicalScreenDescriptor(const gfx::Size& frame_size);
+
+  // Writes the Netscape Application Extension block to the GIF file. This block
+  // allows us to embed information about looping of the frames in the output
+  // animated GIF.
+  // This block is written only once in the entire GIF file.
+  void WriteNetscapeExtension();
+
+  // Writes the Graphic Control Extension block to the GIF file. This block
+  // contains information about the delay after which the video frame should be
+  // rendered. Hence, it uses the given `current_frame_time` to calculate this
+  // delay.
+  // This block repeats once per every received video frame.
+  void WriteGraphicControlExtension(base::TimeTicks current_frame_time);
+
+  // Writes the Image Descriptor block to the GIF file. It describes the bounds
+  // of the frame within the total bounds of the canvas (which was defined
+  // earlier in the Logical Screen Descriptor block). It also specifies whether
+  // the frame has its own Local Color Table.
+  // This block repeats once per every received video frame.
+  void WriteImageDescriptor(const gfx::Size& frame_size,
+                            uint8_t color_bit_depth);
+
+  // Writes the `color_palette_` which has the given `color_bit_depth` to the
+  // GIF file. This function can be uses to write both the Global and Local
+  // color tables. However, we only write Local Color Tables in this
+  // implementation.
+  void WriteColorPalette(uint8_t color_bit_depth);
+
+  // The presentation time of the most recent video frame prior to the one being
+  // encoded at the moment.
+  base::TimeTicks last_frame_time_;
+
+  // The list of colors (up to 256 colors) that will be written to the GIF file
+  // as the color table. These colors are extracted from each received frame
+  // using some color quantization algorithm of choice, which tries to pick the
+  // most important colors that represent the contents of the video frame.
+  ColorTable color_palette_;
+
+  // The color of each pixel in the received video frame is represented as an
+  // index into `color_palette_` after color quantization is complete.
+  ColorIndices pixel_color_indices_;
+
   // Abstracts writing bytes to the GIF file, and takes care of handling IO
   // errors and remaining disk space / DriveFS quota issues.
   GifFileWriter gif_file_writer_;
diff --git a/chromeos/ash/services/recording/gif_encoding_types.h b/chromeos/ash/services/recording/gif_encoding_types.h
new file mode 100644
index 0000000..86a6d60
--- /dev/null
+++ b/chromeos/ash/services/recording/gif_encoding_types.h
@@ -0,0 +1,30 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODING_TYPES_H_
+#define CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODING_TYPES_H_
+
+#include <cstdint>
+#include <vector>
+
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace recording {
+
+// The GIF specs specify a maximum of 12 bits per LZW compression code, so a
+// 16-bit unsigned integer is perfect for this type.
+using LzwCode = uint16_t;
+
+// We have a maximum of 256 colors in our color palette, so an 8-bit unsigned
+// integer is enough to represent indices to these colors.
+using ColorIndex = uint8_t;
+using ColorIndices = std::vector<ColorIndex>;
+
+// Defines a type for a color palette table which will eventually be written to
+// the GIF file.
+using ColorTable = std::vector<SkColor>;
+
+}  // namespace recording
+
+#endif  // CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODING_TYPES_H_
diff --git a/chromeos/ash/services/recording/gif_file_writer.cc b/chromeos/ash/services/recording/gif_file_writer.cc
index d28ba48..7d92a1a 100644
--- a/chromeos/ash/services/recording/gif_file_writer.cc
+++ b/chromeos/ash/services/recording/gif_file_writer.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chromeos/ash/services/recording/gif_file_writer.h"
+
 #include "base/containers/span.h"
 
 namespace recording {
@@ -28,6 +29,20 @@
   WriteBytesAndCheck(base::make_span(buffer, buffer_size));
 }
 
+void GifFileWriter::WriteString(base::StringPiece string) {
+  WriteBytesAndCheck(base::make_span(
+      reinterpret_cast<const uint8_t*>(string.data()), string.size()));
+}
+
+void GifFileWriter::WriteShort(uint16_t value) {
+  WriteByte(value & 0xFF);
+  WriteByte(((value >> 8) & 0xFF));
+}
+
+void GifFileWriter::FlushFile() {
+  gif_file_.Flush();
+}
+
 void GifFileWriter::WriteBytesAndCheck(base::span<const uint8_t> data) {
   if (!gif_file_.WriteAtCurrentPosAndCheck(data)) {
     file_io_helper_.delegate()->NotifyFailure(mojom::RecordingStatus::kIoError);
diff --git a/chromeos/ash/services/recording/gif_file_writer.h b/chromeos/ash/services/recording/gif_file_writer.h
index 6345c9a..6d0fb06 100644
--- a/chromeos/ash/services/recording/gif_file_writer.h
+++ b/chromeos/ash/services/recording/gif_file_writer.h
@@ -8,6 +8,7 @@
 #include <cstdint>
 
 #include "base/files/file.h"
+#include "base/strings/string_piece_forward.h"
 #include "chromeos/ash/services/recording/recording_file_io_helper.h"
 
 namespace recording {
@@ -33,6 +34,16 @@
   // the `gif_file_`.
   void WriteBuffer(const uint8_t* const buffer, size_t buffer_size);
 
+  // Writes the given `string` to the `gif_file_`.
+  void WriteString(base::StringPiece string);
+
+  // Writes the given 16-bit `value` to the `gif_file_` in little endian format
+  // such that the least significant bit gets written first.
+  void WriteShort(uint16_t value);
+
+  // Flushes the contents of the file to the underlying storage.
+  void FlushFile();
+
  private:
   // Writes the given `data` to the `gif_file_` and check for IO errors or disk
   // space / DriveFS quota issues.
diff --git a/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h
index b7094712..addfe192 100644
--- a/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h
+++ b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h
@@ -9,20 +9,12 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
+#include "chromeos/ash/services/recording/gif_encoding_types.h"
 
 namespace recording {
 
 class GifFileWriter;
 
-// The specs specify a maximum of 12 bits per LZW compression code, so a 16-bit
-// unsigned integer is perfect for this type.
-using LzwCode = uint16_t;
-
-// We have a maximum of 256 colors in our color palette, so an 8-bit unsigned
-// integer is enough to represent indices to these colors.
-using ColorIndex = uint8_t;
-using ColorIndices = std::vector<ColorIndex>;
-
 // Background: After color quantization is performed on the video frame, we end
 // up with:
 // 1- A color table (or a color palette) that contains a list of colors (up to
diff --git a/chromeos/ash/services/recording/public/mojom/recording_service.mojom b/chromeos/ash/services/recording/public/mojom/recording_service.mojom
index e0b02a4..603573e 100644
--- a/chromeos/ash/services/recording/public/mojom/recording_service.mojom
+++ b/chromeos/ash/services/recording/public/mojom/recording_service.mojom
@@ -39,6 +39,9 @@
   kLowDiskSpace,
   // Ended due to reaching a critically low DriveFS quota.
   kLowDriveFsQuota,
+  // Ended due to the video encoder not supporting mid-recording
+  // re-configuation.
+  kVideoEncoderReconfigurationFailure,
 };
 
 // Defines the interface for retrieving the remaining amount of DriveFS free
diff --git a/chromeos/ash/services/recording/recording_encoder.h b/chromeos/ash/services/recording/recording_encoder.h
index f311459..700742f5 100644
--- a/chromeos/ash/services/recording/recording_encoder.h
+++ b/chromeos/ash/services/recording/recording_encoder.h
@@ -13,6 +13,7 @@
 #include "chromeos/ash/services/recording/recording_file_io_helper.h"
 #include "media/base/encoder_status.h"
 #include "media/base/video_encoder.h"
+#include "media/base/video_types.h"
 
 namespace media {
 class AudioBus;
@@ -35,6 +36,27 @@
 // underlying actual encoders.
 class RecordingEncoder : public RecordingFileIoHelper::Delegate {
  public:
+  // Defines an interface for the actual encoders types to provide the
+  // capabilities that they support.
+  class Capabilities {
+   public:
+    Capabilities() = default;
+    Capabilities(const Capabilities&) = delete;
+    Capabilities& operator=(const Capabilities&) = delete;
+    virtual ~Capabilities() = default;
+
+    // Returns the pixel format that the encoder supports, which should be used
+    // to initialize the frame sink video capturer in the GPU. Video frames
+    // received from the capturer should be in this pixel format.
+    virtual media::VideoPixelFormat GetSupportedPixelFormat() const = 0;
+
+    // Returns whether the encoder supports size changes of the video frames
+    // during recording (e.g. due to screen rotation, DSF changes, ... etc.).
+    // Size changes during recording require a reconfiguration of the video
+    // encoder.
+    virtual bool SupportsVideoFrameSizeChanges() const = 0;
+  };
+
   explicit RecordingEncoder(OnFailureCallback on_failure_callback);
   RecordingEncoder(const RecordingEncoder&) = delete;
   RecordingEncoder& operator=(const RecordingEncoder&) = delete;
diff --git a/chromeos/ash/services/recording/recording_service.cc b/chromeos/ash/services/recording/recording_service.cc
index 1cb73ef2..5b9f358 100644
--- a/chromeos/ash/services/recording/recording_service.cc
+++ b/chromeos/ash/services/recording/recording_service.cc
@@ -9,12 +9,14 @@
 #include <cstdlib>
 
 #include "base/check.h"
+#include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "chromeos/ash/services/recording/gif_encoder.h"
+#include "chromeos/ash/services/recording/recording_encoder.h"
 #include "chromeos/ash/services/recording/recording_service_constants.h"
 #include "chromeos/ash/services/recording/video_capture_params.h"
 #include "chromeos/ash/services/recording/webm_encoder_muxer.h"
@@ -128,6 +130,15 @@
   std::exit(EXIT_FAILURE);
 }
 
+// Creates the appropriate encoder capabilities based to the type of the given
+// `output_file_path`.
+std::unique_ptr<RecordingEncoder::Capabilities> CreateEncoderCapabilities(
+    const base::FilePath& output_file_path) {
+  return output_file_path.MatchesExtension(".gif")
+             ? GifEncoder::CreateCapabilities()
+             : WebmEncoderMuxer::CreateCapabilities();
+}
+
 // Creates and returns the appropriate encoder based on the type of the given
 // `output_file_path`.
 base::SequenceBound<RecordingEncoder> CreateEncoder(
@@ -475,6 +486,7 @@
   current_video_capture_params_ = std::move(capture_params);
   const bool should_record_audio = audio_stream_factory.is_valid();
 
+  encoder_capabilities_ = CreateEncoderCapabilities(output_file_path);
   encoder_muxer_ = CreateEncoder(
       encoding_task_runner_,
       CreateVideoEncoderOptions(current_video_capture_params_->GetVideoSize()),
@@ -499,6 +511,13 @@
 void RecordingService::ReconfigureVideoEncoder() {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   DCHECK(current_video_capture_params_);
+  DCHECK(encoder_capabilities_);
+
+  if (!encoder_capabilities_->SupportsVideoFrameSizeChanges()) {
+    OnEncodingFailure(
+        mojom::RecordingStatus::kVideoEncoderReconfigurationFailure);
+    return;
+  }
 
   ++number_of_video_encoder_reconfigures_;
   encoder_muxer_.AsyncCall(&RecordingEncoder::InitializeVideoEncoder)
@@ -512,6 +531,7 @@
 
   refresh_timer_.Stop();
   current_video_capture_params_.reset();
+  encoder_capabilities_.reset();
   video_capturer_remote_.reset();
   consumer_receiver_.reset();
 
@@ -524,6 +544,7 @@
     mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   DCHECK(current_video_capture_params_);
+  DCHECK(encoder_capabilities_);
 
   video_capturer_remote_.reset();
   video_capturer_remote_.Bind(std::move(video_capturer));
@@ -532,7 +553,7 @@
   video_capturer_remote_.set_disconnect_handler(base::BindOnce(
       &RecordingService::OnVideoCapturerDisconnected, base::Unretained(this)));
   current_video_capture_params_->InitializeVideoCapturer(
-      video_capturer_remote_);
+      video_capturer_remote_, encoder_capabilities_->GetSupportedPixelFormat());
   video_capturer_remote_->Start(consumer_receiver_.BindNewPipeAndPassRemote(),
                                 viz::mojom::BufferFormatPreference::kDefault);
 
diff --git a/chromeos/ash/services/recording/recording_service.h b/chromeos/ash/services/recording/recording_service.h
index 258b179a..b645371 100644
--- a/chromeos/ash/services/recording/recording_service.h
+++ b/chromeos/ash/services/recording/recording_service.h
@@ -259,6 +259,11 @@
   scoped_refptr<media::AudioCapturerSource> audio_capturer_
       GUARDED_BY_CONTEXT(main_thread_checker_);
 
+  // Abstracts querying the supported capabilities of the currently used encoder
+  // type.
+  std::unique_ptr<RecordingEncoder::Capabilities> encoder_capabilities_
+      GUARDED_BY_CONTEXT(main_thread_checker_);
+
   // Performs all encoding and muxing operations asynchronously on the
   // |encoding_task_runner_|. However, the |encoder_muxer_| object itself is
   // constructed, used, and destroyed on the main thread sequence.
diff --git a/chromeos/ash/services/recording/video_capture_params.cc b/chromeos/ash/services/recording/video_capture_params.cc
index 3eeb0e05..da0b5841 100644
--- a/chromeos/ash/services/recording/video_capture_params.cc
+++ b/chromeos/ash/services/recording/video_capture_params.cc
@@ -281,7 +281,8 @@
 }
 
 void VideoCaptureParams::InitializeVideoCapturer(
-    mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const {
+    mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
+    media::VideoPixelFormat supported_pixel_format) const {
   DCHECK(capturer);
 
   capturer->SetMinCapturePeriod(kMinCapturePeriod);
@@ -290,7 +291,7 @@
   capturer->SetAutoThrottlingEnabled(false);
   // TODO(afakhry): Discuss with //media/ team the implications of color space
   // conversions.
-  capturer->SetFormat(media::PIXEL_FORMAT_I420);
+  capturer->SetFormat(supported_pixel_format);
   capturer->ChangeTarget(
       viz::VideoCaptureTarget(frame_sink_id_, subtree_capture_id_),
       /*crop_version=*/0);
diff --git a/chromeos/ash/services/recording/video_capture_params.h b/chromeos/ash/services/recording/video_capture_params.h
index 50c421c..29e139c 100644
--- a/chromeos/ash/services/recording/video_capture_params.h
+++ b/chromeos/ash/services/recording/video_capture_params.h
@@ -9,8 +9,8 @@
 
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/subtree_capture_id.h"
+#include "media/base/video_types.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/bindings/struct_forward.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom-forward.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -78,10 +78,12 @@
     return current_frame_sink_size_pixels_;
   }
 
-  // Initializes the given |capturer| (passed by ref) according to the capture
-  // parameters. The given |capturer| must be bound before calling this.
+  // Initializes the given `capturer` (passed by ref) according to the capture
+  // parameters. The given `capturer` must be bound before calling this.
+  // The given `supported_pixel_format` will be used to initialize `capturer`.
   void InitializeVideoCapturer(
-      mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const;
+      mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
+      media::VideoPixelFormat supported_pixel_format) const;
 
   // Returns the bounds to which a video frame, whose
   // |original_frame_visible_rect_pixels| is given, should be cropped. If no
diff --git a/chromeos/ash/services/recording/webm_encoder_muxer.cc b/chromeos/ash/services/recording/webm_encoder_muxer.cc
index eecf72b..06c504b 100644
--- a/chromeos/ash/services/recording/webm_encoder_muxer.cc
+++ b/chromeos/ash/services/recording/webm_encoder_muxer.cc
@@ -16,6 +16,7 @@
 #include "media/base/audio_codecs.h"
 #include "media/base/video_codecs.h"
 #include "media/base/video_frame.h"
+#include "media/base/video_types.h"
 #include "media/muxers/file_webm_muxer_delegate.h"
 #include "media/muxers/muxer.h"
 
@@ -40,6 +41,25 @@
 constexpr size_t kMaxDroppedFrames = 4 * kMaxFrameRate;
 
 // -----------------------------------------------------------------------------
+// WebmEncoderCapabilities:
+
+// Implements the capabilities for WebM encoding.
+class WebmEncoderCapabilities : public RecordingEncoder::Capabilities {
+ public:
+  WebmEncoderCapabilities() = default;
+  WebmEncoderCapabilities(const WebmEncoderCapabilities&) = delete;
+  WebmEncoderCapabilities& operator=(const WebmEncoderCapabilities&) = delete;
+  ~WebmEncoderCapabilities() override = default;
+
+  // RecordingEncoder::Capabilities:
+  media::VideoPixelFormat GetSupportedPixelFormat() const override {
+    return media::PIXEL_FORMAT_I420;
+  }
+
+  bool SupportsVideoFrameSizeChanges() const override { return true; }
+};
+
+// -----------------------------------------------------------------------------
 // RecordingMuxerDelegate:
 
 // Defines a delegate for the WebmMuxer which extends the capability of
@@ -114,6 +134,12 @@
       std::move(on_failure_callback));
 }
 
+// static
+std::unique_ptr<RecordingEncoder::Capabilities>
+WebmEncoderMuxer::CreateCapabilities() {
+  return std::make_unique<WebmEncoderCapabilities>();
+}
+
 WebmEncoderMuxer::WebmEncoderMuxer(
     PassKey,
     const media::VideoEncoder::Options& video_encoder_options,
diff --git a/chromeos/ash/services/recording/webm_encoder_muxer.h b/chromeos/ash/services/recording/webm_encoder_muxer.h
index e999855..26b048d 100644
--- a/chromeos/ash/services/recording/webm_encoder_muxer.h
+++ b/chromeos/ash/services/recording/webm_encoder_muxer.h
@@ -75,6 +75,10 @@
       const base::FilePath& webm_file_path,
       OnFailureCallback on_failure_callback);
 
+  // Creates and returns an object that specifies the capabilities of this
+  // encoder.
+  static std::unique_ptr<Capabilities> CreateCapabilities();
+
   WebmEncoderMuxer(
       PassKey,
       const media::VideoEncoder::Options& video_encoder_options,
diff --git a/chromeos/components/quick_answers/result_loader.cc b/chromeos/components/quick_answers/result_loader.cc
index 95f9c3b..1168fa0e 100644
--- a/chromeos/components/quick_answers/result_loader.cc
+++ b/chromeos/components/quick_answers/result_loader.cc
@@ -19,7 +19,6 @@
 using network::ResourceRequest;
 using network::SharedURLLoaderFactory;
 
-// TODO(llin): Update the policy detail after finalizing on the consent check.
 constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotationTag =
     net::DefineNetworkTrafficAnnotation("quick_answers_loader", R"(
           semantics: {
@@ -30,9 +29,11 @@
               "or unit conversion."
             trigger:
               "Right click to trigger context menu."
-            data: "Currently selected text, device language and "
-                  "source language of the selected text "
-                  "is sent to Google API only for translation."
+            data: "Currently selected text is sent to Google API for "
+            "generating answers. Source language of the selected text "
+            "is sent to Google API only for translation and dictionary "
+            "definition. Device language is sent to Google API "
+            "only for translation."
             destination: GOOGLE_OWNED_SERVICE
           }
           policy: {
@@ -47,7 +48,13 @@
                     QuickAnswersEnabled: false
                 }
                 QuickAnswersTranslationEnabled {
-                    QuickAnswersTranslationEnabled: true
+                    QuickAnswersTranslationEnabled: false
+                }
+                QuickAnswersDefinitionEnabled {
+                    QuickAnswersDefinitionEnabled: false
+                }
+                QuickAnswersUnitConversionEnabled {
+                    QuickAnswersUnitConversionEnabled: false
                 }
             }
           })");
diff --git a/chromeos/crosapi/mojom/prefs.mojom b/chromeos/crosapi/mojom/prefs.mojom
index 28f7025..e3b5fd5 100644
--- a/chromeos/crosapi/mojom/prefs.mojom
+++ b/chromeos/crosapi/mojom/prefs.mojom
@@ -95,6 +95,10 @@
   [MinVersion=7] kDnsOverHttpsSalt = 33,
   // M110: ash::prefs::kUserGeolocationAllowed
   [MinVersion=8] kGeolocationAllowed = 34,
+  // M112: prefs::kMultitaskMenuNudgeClamshellShownCount
+  [MinVersion=9] kMultitaskMenuNudgeClamshellShownCount = 35,
+  // M112: prefs::kMultitaskMenuNudgeClamshellLastShown
+  [MinVersion=9] kMultitaskMenuNudgeClamshellLastShown = 36,
 };
 
 // Information about who or what is controlling a particular pref. This is used
diff --git a/chromeos/ui/frame/BUILD.gn b/chromeos/ui/frame/BUILD.gn
index 1cead62..77c567e 100644
--- a/chromeos/ui/frame/BUILD.gn
+++ b/chromeos/ui/frame/BUILD.gn
@@ -85,6 +85,10 @@
     "//ui/views/window/vector_icons",
     "//ui/wm/public",
   ]
+
+  if (is_chromeos_ash) {
+    deps += [ "//ash/constants" ]
+  }
 }
 
 source_set("test_support") {
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc
index aba6911..373104f46 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc
@@ -21,6 +21,10 @@
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/widget/widget.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_pref_names.h"
+#endif
+
 namespace chromeos {
 
 namespace {
@@ -119,14 +123,20 @@
   display::Screen::GetScreen()->RemoveObserver(this);
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 // static
 void MultitaskMenuNudgeController::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
-  registry->RegisterIntegerPref(kClamshellShownCountPrefName, 0);
-  registry->RegisterIntegerPref(kTabletShownCountPrefName, 0);
-  registry->RegisterTimePref(kClamshellLastShownPrefName, base::Time());
-  registry->RegisterTimePref(kTabletLastShownPrefName, base::Time());
+  registry->RegisterIntegerPref(
+      ash::prefs::kMultitaskMenuNudgeClamshellShownCount, 0);
+  registry->RegisterIntegerPref(ash::prefs::kMultitaskMenuNudgeTabletShownCount,
+                                0);
+  registry->RegisterTimePref(ash::prefs::kMultitaskMenuNudgeClamshellLastShown,
+                             base::Time());
+  registry->RegisterTimePref(ash::prefs::kMultitaskMenuNudgeTabletLastShown,
+                             base::Time());
 }
+#endif
 
 void MultitaskMenuNudgeController::MaybeShowNudge(aura::Window* window) {
   MaybeShowNudge(window, /*anchor_view=*/nullptr);
@@ -139,10 +149,7 @@
     return;
   }
 
-  // TODO(sammiequon): Delegate is not available for lacros yet.
-  if (!g_delegate_instance) {
-    return;
-  }
+  DCHECK(g_delegate_instance);
 
   // If the window is not visible, do not show the nudge.
   if (!window->IsVisible()) {
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h
index 9c4128af..ea054acb 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h
@@ -56,27 +56,15 @@
     Delegate();
   };
 
-  // The name of an integer pref that counts the number of times we have shown
-  // the multitask menu education nudge.
-  static constexpr char kClamshellShownCountPrefName[] =
-      "ash.wm_nudge.multitask_menu_nudge_count";
-  static constexpr char kTabletShownCountPrefName[] =
-      "cros.wm_nudge.tablet_multitask_nudge_count";
-
-  // The name of a time pref that stores the time we last showed the multitask
-  // menu education nudge.
-  static constexpr char kClamshellLastShownPrefName[] =
-      "ash.wm_nudge.multitask_menu_nudge_last_shown";
-  static constexpr char kTabletLastShownPrefName[] =
-      "cros.wm_nudge.tablet_multitask_nudge_last_shown";
-
   MultitaskMenuNudgeController();
   MultitaskMenuNudgeController(const MultitaskMenuNudgeController&) = delete;
   MultitaskMenuNudgeController& operator=(const MultitaskMenuNudgeController&) =
       delete;
   ~MultitaskMenuNudgeController() override;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+#endif
 
   // Attempts to show the nudge. Reads preferences and then calls
   // `OnGetPreferences()`.
diff --git a/components/BUILD.gn b/components/BUILD.gn
index dee253d4..7816b4b 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -339,13 +339,13 @@
       "//components/cast_streaming:unit_tests",
       "//components/certificate_transparency:unit_tests",
       "//components/content_capture/browser:unit_tests",
+      "//components/content_relationship_verification:unit_tests",
       "//components/content_settings/browser:unit_tests",
       "//components/contextual_search/core/browser:unit_tests",
       "//components/continuous_search/browser:unit_tests",
       "//components/continuous_search/common:unit_tests",
       "//components/custom_handlers:unit_tests",
       "//components/devtools/simple_devtools_protocol_client:unit_tests",
-      "//components/digital_asset_links:unit_tests",
       "//components/discardable_memory/client:unit_tests",
       "//components/discardable_memory/common:unit_tests",
       "//components/discardable_memory/service:unit_tests",
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.cc b/components/autofill/content/browser/content_autofill_driver_factory.cc
index a789015f..ec6448c 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory.cc
@@ -90,7 +90,11 @@
       client_(client),
       driver_init_hook_(std::move(driver_init_hook)) {}
 
-ContentAutofillDriverFactory::~ContentAutofillDriverFactory() = default;
+ContentAutofillDriverFactory::~ContentAutofillDriverFactory() {
+  for (Observer& observer : observers_) {
+    observer.OnContentAutofillDriverFactoryDestroyed(*this);
+  }
+}
 
 std::unique_ptr<ContentAutofillDriver>
 ContentAutofillDriverFactory::CreateDriver(content::RenderFrameHost* rfh) {
@@ -129,6 +133,9 @@
     // 5. `render_frame_host->~RenderFrameHostImpl()` finishes.
     if (render_frame_host->IsRenderFrameLive()) {
       driver = CreateDriver(render_frame_host);
+      for (Observer& observer : observers_) {
+        observer.OnContentAutofillDriverCreated(*this, *driver);
+      }
       DCHECK_EQ(driver_map_.find(render_frame_host)->second.get(),
                 driver.get());
     } else {
@@ -169,6 +176,9 @@
     driver->renderer_events().HidePopup();
   }
 
+  for (Observer& observer : observers_) {
+    observer.OnContentAutofillDriverWillBeDeleted(*this, *driver);
+  }
   driver_map_.erase(it);
 }
 
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.h b/components/autofill/content/browser/content_autofill_driver_factory.h
index 0c480edf..a1e80d2 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.h
+++ b/components/autofill/content/browser/content_autofill_driver_factory.h
@@ -44,6 +44,33 @@
   using DriverInitCallback =
       base::RepeatingCallback<void(ContentAutofillDriver*)>;
 
+  // Observer of ContentAutofillDriverFactory events.
+  //
+  // Using this observer is preferable over registering a WebContentsObserver
+  // and calling ContentAutofillDriverFactory::DriverForFrame() in the
+  // WebContentsObserver events.
+  class Observer : public base::CheckedObserver {
+   public:
+    // Called during destruction of the ContentAutofillDriverFactory. No
+    // members of `factory` should be accessed in this event handler.
+    virtual void OnContentAutofillDriverFactoryDestroyed(
+        ContentAutofillDriverFactory& factory) {}
+
+    // Called right after the driver has been created.
+    // At the time of this event, the `driver` object is already fully alive and
+    // `factory.DriverForFrame(driver.render_frame_host()) == &driver` holds.
+    virtual void OnContentAutofillDriverCreated(
+        ContentAutofillDriverFactory& factory,
+        ContentAutofillDriver& driver) {}
+
+    // Called right before the driver's RenderFrameHost is deleted.
+    // At the time of this event, the `driver` object is still fully alive and
+    // `factory.DriverForFrame(driver.render_frame_host()) == &driver` holds.
+    virtual void OnContentAutofillDriverWillBeDeleted(
+        ContentAutofillDriverFactory& factory,
+        ContentAutofillDriver& driver) {}
+  };
+
   static ContentAutofillDriverFactory* FromWebContents(
       content::WebContents* contents);
 
@@ -75,6 +102,12 @@
 
   AutofillClient* client() { return client_; }
 
+  void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
+
+  void RemoveObserver(Observer* observer) {
+    observers_.RemoveObserver(observer);
+  }
+
  private:
   friend class ContentAutofillDriverFactoryTestApi;
 
@@ -88,13 +121,15 @@
   // Must be destroyed after |driver_map_|'s elements.
   ContentAutofillRouter router_;
 
-  // The list of drivers, one for each frame in the WebContents.
+  // Owns the drivers, one for each frame in the WebContents.
   // Should be empty at destruction time because its elements are erased in
   // RenderFrameDeleted(). In case it is not empty, is must be destroyed before
   // |router_| because ~ContentAutofillDriver() may access |router_|.
   std::unordered_map<content::RenderFrameHost*,
                      std::unique_ptr<ContentAutofillDriver>>
       driver_map_;
+
+  base::ObserverList<Observer> observers_;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
index 6bd709e..a94dba1 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
@@ -30,6 +30,7 @@
 using testing::_;
 using testing::AtLeast;
 using testing::Between;
+using testing::Ref;
 
 namespace autofill {
 
@@ -40,6 +41,25 @@
   MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override));
 };
 
+class MockContentAutofillDriverFactoryObserver
+    : public ContentAutofillDriverFactory::Observer {
+ public:
+  MOCK_METHOD(void,
+              OnContentAutofillDriverFactoryDestroyed,
+              (ContentAutofillDriverFactory & factory),
+              (override));
+  MOCK_METHOD(void,
+              OnContentAutofillDriverCreated,
+              (ContentAutofillDriverFactory & factory,
+               ContentAutofillDriver& driver),
+              (override));
+  MOCK_METHOD(void,
+              OnContentAutofillDriverWillBeDeleted,
+              (ContentAutofillDriverFactory & factory,
+               ContentAutofillDriver& driver),
+              (override));
+};
+
 class MockAutofillAgent : public mojom::AutofillAgent {
  public:
   void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
@@ -434,4 +454,49 @@
   testing::Mock::VerifyAndClearExpectations(agent_.get());
 }
 
+// Tests the notifications of ContentAutofillDriverFactory::Observer.
+class ContentAutofillDriverFactoryTest_Observer
+    : public ContentAutofillDriverFactoryTest {
+ public:
+  void SetUp() override {
+    ContentAutofillDriverFactoryTest::SetUp();
+    factory_->AddObserver(&observer_);
+  }
+
+  void TearDown() override {
+    if (factory_) {
+      factory_->RemoveObserver(&observer_);
+    }
+    ContentAutofillDriverFactoryTest::TearDown();
+  }
+
+  MockContentAutofillDriverFactoryObserver observer_;
+};
+
+auto IsKnownDriver(ContentAutofillDriverFactory* factory) {
+  return testing::Truly([factory](ContentAutofillDriver& driver) {
+    return factory->DriverForFrame(driver.render_frame_host()) == &driver;
+  });
+}
+
+TEST_F(ContentAutofillDriverFactoryTest_Observer, FactoryDestroyed) {
+  EXPECT_CALL(observer_,
+              OnContentAutofillDriverFactoryDestroyed(Ref(*factory_)));
+  factory_.reset();
+}
+
+TEST_F(ContentAutofillDriverFactoryTest_Observer, DriverCreated) {
+  EXPECT_CALL(observer_, OnContentAutofillDriverCreated(
+                             Ref(*factory_), IsKnownDriver(factory_.get())));
+  NavigateMainFrame("https://a.com/");
+}
+
+TEST_F(ContentAutofillDriverFactoryTest_Observer, DriverDeleted) {
+  EXPECT_CALL(observer_, OnContentAutofillDriverCreated);
+  EXPECT_CALL(observer_, OnContentAutofillDriverWillBeDeleted(
+                             Ref(*factory_), IsKnownDriver(factory_.get())));
+  NavigateMainFrame("https://a.com/");
+  factory_->RenderFrameDeleted(main_rfh());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 8a0b69b3..e44db96e 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -70,8 +70,7 @@
   // - if the request in AutofillDownloadManager was not successful (i.e. no 2XX
   //   response code or a null response body).
   //
-  // The main purpose are unit tests. New pairs of events may be added as
-  // needed.
+  // New pairs of events may be added as needed.
   class Observer : public base::CheckedObserver {
    public:
     virtual void OnAutofillManagerDestroyed(AutofillManager& manager) {}
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 82b000f..92d2fd8 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -586,13 +586,9 @@
   // country or the app locale. For the `variation_country_code` to take
   // precedence over the app locale, country code complemention needs to happen
   // before `SetPhoneNumber()`.
-  bool complement_country_early =
-      base::FeatureList::IsEnabled(features::kAutofillComplementCountryEarly);
-  if (complement_country_early) {
-    import_metadata.did_complement_country =
-        should_complement_country &&
-        ComplementCountry(candidate_profile, predicted_country_code);
-  }
+  import_metadata.did_complement_country =
+      should_complement_country &&
+      ComplementCountry(candidate_profile, predicted_country_code);
 
   if (!SetPhoneNumber(candidate_profile, combined_phone)) {
     candidate_profile.ClearFields({PHONE_HOME_WHOLE_NUMBER});
@@ -629,12 +625,6 @@
                                 app_locale_, /*import_log_buffer=*/nullptr);
   }
 
-  if (!complement_country_early) {
-    import_metadata.did_complement_country =
-        should_complement_country &&
-        ComplementCountry(candidate_profile, predicted_country_code);
-  }
-
   // This relies on the profile's country code and must be done strictly after
   // `ComplementCountry()`.
   RemoveInaccessibleProfileValues(candidate_profile);
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 967ce37..b920212 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -840,10 +840,10 @@
   ImportWithCountry("", {kDefaultGermanProfile});
 }
 
-// Tests that by complementing the country before setting the phone number,
-// the variation country code is preferred over the app locale while parsing
-// nationally formatted phone numbers.
-TEST_P(FormDataImporterTest, ComplementCountryEarly) {
+// Tests that the country is complemented before parsing the phone number. This
+// is important, since the phone number validation relies on the profile's
+// country for nationally formatted numbers.
+TEST_P(FormDataImporterTest, ComplementCountry_PhoneNumberParsing) {
   // This is a nationally formatted German phone number, which libphonenumber
   // doesn't parse under the "US" region.
   const char* kNationalNumber = "01578 7912345";
@@ -865,38 +865,16 @@
   // imported country will have country = "DE" assigned.
   autofill_client_->SetVariationConfigCountryCode("DE");
 
-  {
-    base::test::ScopedFeatureList complement_country_early_feature;
-    complement_country_early_feature.InitAndDisableFeature(
-        features::kAutofillComplementCountryEarly);
-
-    // Without the feature, the phone number parsing logic defaults to the
-    // "en_US" locale. Thus, parsing fails and the phone number is removed.
-    base::HistogramTester histogram_tester;
-    expected_profile.ClearFields({PHONE_HOME_WHOLE_NUMBER});
-    ExtractAddressProfilesAndVerifyExpectation(*form_structure,
-                                               {expected_profile});
-    EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramName),
-                testing::UnorderedElementsAre(base::Bucket(false, 1)));
-  }
-
-  {
-    base::test::ScopedFeatureList complement_country_early_feature;
-    complement_country_early_feature.InitAndEnableFeature(
-        features::kAutofillComplementCountryEarly);
-
-    // With the feature enabled, the country complemention happens first. Thus,
-    // at the time the number is parsed, we correctly apply the German rules.
-    base::HistogramTester histogram_tester;
-    // The `expected_profile` can successfully parse the number, as the
-    // profile's country is "DE".
-    EXPECT_TRUE(expected_profile.SetInfo(
-        PHONE_HOME_WHOLE_NUMBER, base::UTF8ToUTF16(kNationalNumber), kLocale));
-    ExtractAddressProfilesAndVerifyExpectation(*form_structure,
-                                               {expected_profile});
-    EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramName),
-                testing::UnorderedElementsAre(base::Bucket(true, 1)));
-  }
+  // Country complemention happens before parsing the phone number. Thus, at the
+  // time the number is parsed, we correctly apply the German rules.
+  base::HistogramTester histogram_tester;
+  // The `expected_profile` can successfully parse the number, as the
+  // profile's country is "DE".
+  EXPECT_TRUE(expected_profile.SetInfo(
+      PHONE_HOME_WHOLE_NUMBER, base::UTF8ToUTF16(kNationalNumber), kLocale));
+  ExtractAddressProfilesAndVerifyExpectation(*form_structure,
+                                             {expected_profile});
+  histogram_tester.ExpectUniqueSample(kHistogramName, true, 1);
 }
 
 // Tests how invalid countries in submitted forms are treated depending on
@@ -4207,10 +4185,8 @@
 
 // Tests that a complemented country is discarded in favour of an observed one.
 TEST_P(FormDataImporterTest, MultiStepImport_ComplementCountryEarly) {
-  base::test::ScopedFeatureList features;
-  features.InitWithFeatures({features::kAutofillEnableMultiStepImports,
-                             features::kAutofillComplementCountryEarly},
-                            {});
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(features::kAutofillEnableMultiStepImports);
 
   // Import a profile fragment with country information.
   TypeValuePairs type_value_pairs =
diff --git a/components/autofill/core/browser/form_data_importer_utils.cc b/components/autofill/core/browser/form_data_importer_utils.cc
index 2154500d..db016f3 100644
--- a/components/autofill/core/browser/form_data_importer_utils.cc
+++ b/components/autofill/core/browser/form_data_importer_utils.cc
@@ -244,22 +244,14 @@
   auto candidate = multistep_candidates_.begin();
   AutofillProfile completed_profile = profile;
   ProfileImportMetadata completed_metadata = import_metadata;
-  // If `complement_country_early` is enabled, merging might fail due to an
-  // incorrectly complemented country in one of the merge candidates.
-  // Without the feature, the country complement logic has not happened yet.
-  bool complement_country_early =
-      base::FeatureList::IsEnabled(features::kAutofillComplementCountryEarly);
-  DCHECK(complement_country_early ||
-         !completed_metadata.did_complement_country);
-  while (candidate != multistep_candidates_.end()) {
-    if (!comparator_.AreMergeable(completed_profile, candidate->profile) &&
-        (!complement_country_early ||
-         !MergeableByRemovingIncorrectlyComplementedCountry(
-             completed_profile, completed_metadata.did_complement_country,
-             candidate->profile,
-             candidate->import_metadata.did_complement_country))) {
-      break;
-    }
+  // Merging might fail due to an incorrectly complemented country in one of the
+  // merge candidates. In this case, try removing the complemented country.
+  while (candidate != multistep_candidates_.end() &&
+         (comparator_.AreMergeable(completed_profile, candidate->profile) ||
+          MergeableByRemovingIncorrectlyComplementedCountry(
+              completed_profile, completed_metadata.did_complement_country,
+              candidate->profile,
+              candidate->import_metadata.did_complement_country))) {
     completed_profile.MergeDataFrom(candidate->profile, app_locale_);
     MergeImportMetadata(candidate->import_metadata, completed_metadata);
     candidate++;
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 1eb16a6..e5e65011 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -611,15 +611,15 @@
 CreditCardFidoAuthenticator::ParseCredentialDescriptor(
     const base::Value& key_info) {
   std::vector<uint8_t> credential_id;
-  const auto* id = key_info.FindStringKey("credential_id");
+  const auto* id = key_info.GetDict().FindString("credential_id");
   DCHECK(id);
   credential_id = Base64ToBytes(*id);
 
   base::flat_set<device::FidoTransportProtocol> authenticator_transports;
-  const auto* transports = key_info.FindKeyOfType(
-      "authenticator_transport_support", base::Value::Type::LIST);
-  if (transports && !transports->GetList().empty()) {
-    for (const base::Value& transport_type : transports->GetList()) {
+  const auto* transports =
+      key_info.GetDict().FindList("authenticator_transport_support");
+  if (transports && !transports->empty()) {
+    for (const base::Value& transport_type : *transports) {
       absl::optional<device::FidoTransportProtocol> protocol =
           device::ConvertToFidoTransportProtocol(
               base::ToLowerASCII(transport_type.GetString()));
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 22d9cfe3..758ac85b 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -82,14 +82,6 @@
              "AutofillInferCountryCallingCode",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, complementing the country happens before setting the phone number
-// on profile import. This way, the variation country code takes precedence over
-// the app locale.
-// TODO(crbug.com/1295721): Cleanup when launched.
-BASE_FEATURE(kAutofillComplementCountryEarly,
-             "AutofillComplementCountryEarly",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // If enabled, label inference considers strings entirely made up of  '(', ')'
 // and '-' as valid labels.
 // TODO(crbug.com/1311937): Cleanup when launched.
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index b969b81..22be90ff 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -37,8 +37,6 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillInferCountryCallingCode);
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillComplementCountryEarly);
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillConsiderPhoneNumberSeparatorsValidLabels);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillDeferSubmissionClassificationAfterAjax);
diff --git a/components/blocked_content/popup_navigation_delegate.h b/components/blocked_content/popup_navigation_delegate.h
index 55ea3357..52eb3ce 100644
--- a/components/blocked_content/popup_navigation_delegate.h
+++ b/components/blocked_content/popup_navigation_delegate.h
@@ -33,7 +33,7 @@
   virtual bool GetOriginalUserGesture() = 0;
 
   // Gets the URL to be loaded.
-  virtual const GURL& GetURL() = 0;
+  virtual GURL GetURL() = 0;
 
   // Performs the navigation.
   struct NavigateResult {
diff --git a/components/blocked_content/test/test_popup_navigation_delegate.cc b/components/blocked_content/test/test_popup_navigation_delegate.cc
index 640eaba6..91cc90a9 100644
--- a/components/blocked_content/test/test_popup_navigation_delegate.cc
+++ b/components/blocked_content/test/test_popup_navigation_delegate.cc
@@ -23,7 +23,7 @@
   return true;
 }
 
-const GURL& TestPopupNavigationDelegate::GetURL() {
+GURL TestPopupNavigationDelegate::GetURL() {
   return url_;
 }
 
diff --git a/components/blocked_content/test/test_popup_navigation_delegate.h b/components/blocked_content/test/test_popup_navigation_delegate.h
index b34858c..f147d135 100644
--- a/components/blocked_content/test/test_popup_navigation_delegate.h
+++ b/components/blocked_content/test/test_popup_navigation_delegate.h
@@ -32,7 +32,7 @@
   // PopupNavigationDelegate:
   content::RenderFrameHost* GetOpener() override;
   bool GetOriginalUserGesture() override;
-  const GURL& GetURL() override;
+  GURL GetURL() override;
   NavigateResult NavigateWithGesture(
       const blink::mojom::WindowFeatures& window_features,
       absl::optional<WindowOpenDisposition> updated_disposition) override;
diff --git a/components/bookmarks/browser/bookmark_storage_unittest.cc b/components/bookmarks/browser/bookmark_storage_unittest.cc
index bd07d77..5d488d2 100644
--- a/components/bookmarks/browser/bookmark_storage_unittest.cc
+++ b/components/bookmarks/browser/bookmark_storage_unittest.cc
@@ -95,7 +95,7 @@
       bookmarks_file_path.ReplaceExtension(FILE_PATH_LITERAL("bak"));
 
   // Create a dummy JSON file, to verify backups are created.
-  ASSERT_NE(0, base::WriteFile(bookmarks_file_path, "{}", 2));
+  ASSERT_TRUE(base::WriteFile(bookmarks_file_path, "{}"));
 
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
diff --git a/components/cast_streaming/OWNERS b/components/cast_streaming/OWNERS
index 92be789a..f2aea22e 100644
--- a/components/cast_streaming/OWNERS
+++ b/components/cast_streaming/OWNERS
@@ -2,3 +2,4 @@
 fdegans@chromium.org
 rwkeane@google.com
 ddorwin@chromium.org
+mfoltz@chromium.org
diff --git a/components/digital_asset_links/BUILD.gn b/components/content_relationship_verification/BUILD.gn
similarity index 76%
rename from components/digital_asset_links/BUILD.gn
rename to components/content_relationship_verification/BUILD.gn
index a317ea5..f681c1a 100644
--- a/components/digital_asset_links/BUILD.gn
+++ b/components/content_relationship_verification/BUILD.gn
@@ -2,10 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("digital_asset_links") {
+source_set("content_relationship_verification") {
   sources = [
-    "digital_asset_links_constants.cc",
-    "digital_asset_links_constants.h",
+    "content_relationship_verification_constants.cc",
+    "content_relationship_verification_constants.h",
     "digital_asset_links_handler.cc",
     "digital_asset_links_handler.h",
     "response_header_verifier.cc",
@@ -28,7 +28,8 @@
       "origin_verifier.cc",
       "origin_verifier.h",
     ]
-    deps += [ "//components/digital_asset_links/android:jni_headers" ]
+    deps +=
+        [ "//components/content_relationship_verification/android:jni_headers" ]
   }
 }
 
@@ -41,7 +42,7 @@
   ]
 
   deps = [
-    ":digital_asset_links",
+    ":content_relationship_verification",
     "//base",
     "//content/test:test_support",
     "//net",
@@ -56,7 +57,8 @@
 
   android_library("java") {
     deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
-    srcjar_deps = [ "//components/digital_asset_links:enums_srcjar" ]
+    srcjar_deps =
+        [ "//components/content_relationship_verification:enums_srcjar" ]
   }
 
   java_cpp_enum("enums_srcjar") {
diff --git a/components/digital_asset_links/DEPS b/components/content_relationship_verification/DEPS
similarity index 100%
rename from components/digital_asset_links/DEPS
rename to components/content_relationship_verification/DEPS
diff --git a/components/digital_asset_links/DIR_METADATA b/components/content_relationship_verification/DIR_METADATA
similarity index 100%
rename from components/digital_asset_links/DIR_METADATA
rename to components/content_relationship_verification/DIR_METADATA
diff --git a/components/digital_asset_links/OWNERS b/components/content_relationship_verification/OWNERS
similarity index 100%
rename from components/digital_asset_links/OWNERS
rename to components/content_relationship_verification/OWNERS
diff --git a/components/content_relationship_verification/README.md b/components/content_relationship_verification/README.md
new file mode 100644
index 0000000..8c52735
--- /dev/null
+++ b/components/content_relationship_verification/README.md
@@ -0,0 +1,21 @@
+# Content Relationship Verification
+
+This component contains utilities to verify relationships between Web content and the embedder.
+
+## Verification methods
+
+- Digital Asset Links
+
+    Utilities to facilitate making requests to the DigitalAssetLinks API on the Web.
+
+    See [documentation](https://developers.google.com/digital-asset-links/v1/getting-started).
+
+
+- Response Header Verification
+
+    Class that contains utilities to parse the `X-Embedder-Ancestors`- response header.
+
+
+## Browser URL Loader Throttle
+
+URL loader throttle that aborts loading of http responses when no relationship between Web content and embedder was verified.
\ No newline at end of file
diff --git a/components/content_relationship_verification/android/BUILD.gn b/components/content_relationship_verification/android/BUILD.gn
new file mode 100644
index 0000000..f33972b
--- /dev/null
+++ b/components/content_relationship_verification/android/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/components/content_relationship_verification/OriginVerificationScheduler.java",
+    "java/src/org/chromium/components/content_relationship_verification/OriginVerifier.java",
+    "java/src/org/chromium/components/content_relationship_verification/OriginVerifierHelper.java",
+    "java/src/org/chromium/components/content_relationship_verification/Relationship.java",
+    "java/src/org/chromium/components/content_relationship_verification/VerificationResultStore.java",
+  ]
+  deps = [
+    "//base:base_java",
+    "//base:jni_java",
+    "//build/android:build_java",
+    "//components/content_relationship_verification:java",
+    "//components/embedder_support/android:util_java",
+    "//content/public/android:content_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+robolectric_library("junit_test_support") {
+  sources = [ "java/src/org/chromium/components/content_relationship_verification/OriginVerifierUnitTestSupport.java" ]
+  deps = [
+    ":java",
+    "//components/embedder_support/android:util_java",
+    "//third_party/androidx:androidx_browser_browser_java",
+    "//third_party/mockito:mockito_java",
+  ]
+}
+
+generate_jni("jni_headers") {
+  sources = [ "java/src/org/chromium/components/content_relationship_verification/OriginVerifier.java" ]
+}
diff --git a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerificationScheduler.java b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerificationScheduler.java
similarity index 97%
rename from components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerificationScheduler.java
rename to components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerificationScheduler.java
index 0189713fe..7bf061e 100644
--- a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerificationScheduler.java
+++ b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerificationScheduler.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.components.digital_asset_links;
+package org.chromium.components.content_relationship_verification;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
diff --git a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifier.java b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifier.java
similarity index 98%
rename from components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifier.java
rename to components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifier.java
index c9c0009..f8f098d 100644
--- a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifier.java
+++ b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifier.java
@@ -1,8 +1,8 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.components.digital_asset_links;
+package org.chromium.components.content_relationship_verification;
 
 import android.content.pm.PackageManager;
 import android.net.Uri;
diff --git a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifierHelper.java b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifierHelper.java
similarity index 97%
rename from components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifierHelper.java
rename to components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifierHelper.java
index 3aedf0e..8f944c1 100644
--- a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifierHelper.java
+++ b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifierHelper.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.components.digital_asset_links;
+package org.chromium.components.content_relationship_verification;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifierUnitTestSupport.java b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifierUnitTestSupport.java
similarity index 97%
rename from components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifierUnitTestSupport.java
rename to components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifierUnitTestSupport.java
index 31527fd..65ccd5e9 100644
--- a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/OriginVerifierUnitTestSupport.java
+++ b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/OriginVerifierUnitTestSupport.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.components.digital_asset_links;
+package org.chromium.components.content_relationship_verification;
 
 import android.content.pm.PackageInfo;
 import android.content.pm.Signature;
diff --git a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/Relationship.java b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/Relationship.java
similarity index 95%
rename from components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/Relationship.java
rename to components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/Relationship.java
index da4fcdc..bfd01cb 100644
--- a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/Relationship.java
+++ b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/Relationship.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.components.digital_asset_links;
+package org.chromium.components.content_relationship_verification;
 
 import org.chromium.components.embedder_support.util.Origin;
 
diff --git a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/VerificationResultStore.java b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/VerificationResultStore.java
similarity index 97%
rename from components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/VerificationResultStore.java
rename to components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/VerificationResultStore.java
index afe2507..2aa66b6 100644
--- a/components/digital_asset_links/android/java/src/org/chromium/components/digital_asset_links/VerificationResultStore.java
+++ b/components/content_relationship_verification/android/java/src/org/chromium/components/content_relationship_verification/VerificationResultStore.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.components.digital_asset_links;
+package org.chromium.components.content_relationship_verification;
 
 import androidx.annotation.VisibleForTesting;
 
diff --git a/components/digital_asset_links/browser_url_loader_throttle.cc b/components/content_relationship_verification/browser_url_loader_throttle.cc
similarity index 87%
rename from components/digital_asset_links/browser_url_loader_throttle.cc
rename to components/content_relationship_verification/browser_url_loader_throttle.cc
index ae2f6fa..72e9747 100644
--- a/components/digital_asset_links/browser_url_loader_throttle.cc
+++ b/components/content_relationship_verification/browser_url_loader_throttle.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/digital_asset_links/browser_url_loader_throttle.h"
+#include "components/content_relationship_verification/browser_url_loader_throttle.h"
 
 #include "base/android/build_info.h"
 #include "base/check_op.h"
@@ -10,8 +10,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/trace_event/trace_event.h"
-#include "components/digital_asset_links/digital_asset_links_constants.h"
-#include "components/digital_asset_links/response_header_verifier.h"
+#include "components/content_relationship_verification/content_relationship_verification_constants.h"
+#include "components/content_relationship_verification/response_header_verifier.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "net/log/net_log_event_type.h"
 #include "net/url_request/redirect_info.h"
@@ -19,7 +19,7 @@
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 
 BrowserURLLoaderThrottle::OriginVerificationSchedulerBridge::
     OriginVerificationSchedulerBridge() = default;
@@ -46,7 +46,7 @@
   std::string header_value;
   response_head.headers->GetNormalizedHeader(kEmbedderAncestorHeader,
                                              &header_value);
-  return digital_asset_links::ResponseHeaderVerifier::Verify(
+  return content_relationship_verification::ResponseHeaderVerifier::Verify(
       base::android::BuildInfo::GetInstance()->host_package_name(),
       header_value);
 }
@@ -105,7 +105,7 @@
     delegate_->Resume();
     return;
   }
-  delegate_->CancelWithError(kNetErrorCodeForDigitalAssetLinks,
+  delegate_->CancelWithError(kNetErrorCodeForContentRelationshipVerification,
                              kCustomCancelReasonForURLLoader);
 }
 
@@ -113,4 +113,4 @@
   return "DigitalAssetLinksBrowserThrottle";
 }
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
diff --git a/components/digital_asset_links/browser_url_loader_throttle.h b/components/content_relationship_verification/browser_url_loader_throttle.h
similarity index 89%
rename from components/digital_asset_links/browser_url_loader_throttle.h
rename to components/content_relationship_verification/browser_url_loader_throttle.h
index ca6572d..b4803fb 100644
--- a/components/digital_asset_links/browser_url_loader_throttle.h
+++ b/components/content_relationship_verification/browser_url_loader_throttle.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DIGITAL_ASSET_LINKS_BROWSER_URL_LOADER_THROTTLE_H_
-#define COMPONENTS_DIGITAL_ASSET_LINKS_BROWSER_URL_LOADER_THROTTLE_H_
+#ifndef COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_BROWSER_URL_LOADER_THROTTLE_H_
+#define COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_BROWSER_URL_LOADER_THROTTLE_H_
 
 #include <memory>
 
@@ -16,7 +16,7 @@
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "url/gurl.h"
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 
 // TODO(crbug.com/1376958): Add CSP as method to allow content access in this
 // throttle and then move it to components/third_party_restrictions.
@@ -89,6 +89,6 @@
   base::WeakPtrFactory<BrowserURLLoaderThrottle> weak_factory_{this};
 };
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
 
-#endif  // COMPONENTS_DIGITAL_ASSET_LINKS_BROWSER_URL_LOADER_THROTTLE_H_
+#endif  // COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_BROWSER_URL_LOADER_THROTTLE_H_
diff --git a/components/content_relationship_verification/content_relationship_verification_constants.cc b/components/content_relationship_verification/content_relationship_verification_constants.cc
new file mode 100644
index 0000000..88e7f51
--- /dev/null
+++ b/components/content_relationship_verification/content_relationship_verification_constants.cc
@@ -0,0 +1,17 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/content_relationship_verification/content_relationship_verification_constants.h"
+
+#include "net/base/net_errors.h"
+
+namespace content_relationship_verification {
+
+const char kCustomCancelReasonForURLLoader[] =
+    "ContentRelationshipVerification";
+
+const int kNetErrorCodeForContentRelationshipVerification =
+    net::ERR_ACCESS_DENIED;
+
+}  // namespace content_relationship_verification
diff --git a/components/content_relationship_verification/content_relationship_verification_constants.h b/components/content_relationship_verification/content_relationship_verification_constants.h
new file mode 100644
index 0000000..293471b
--- /dev/null
+++ b/components/content_relationship_verification/content_relationship_verification_constants.h
@@ -0,0 +1,21 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_CONTENT_RELATIONSHIP_VERIFICATION_CONSTANTS_H_
+#define COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_CONTENT_RELATIONSHIP_VERIFICATION_CONSTANTS_H_
+
+namespace content_relationship_verification {
+
+// When a network::mojom::URLLoader is cancelled because of content relationship
+// verification, this custom cancellation reason could be used to notify the
+// implementation side. Please see
+// network::mojom::URLLoader::kClientDisconnectReason for more details.
+extern const char kCustomCancelReasonForURLLoader[];
+
+// error_code to use when content relationship verification blocks a request.
+extern const int kNetErrorCodeForContentRelationshipVerification;
+
+}  // namespace content_relationship_verification
+
+#endif  // COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_CONTENT_RELATIONSHIP_VERIFICATION_CONSTANTS_H_
diff --git a/components/digital_asset_links/digital_asset_links_handler.cc b/components/content_relationship_verification/digital_asset_links_handler.cc
similarity index 96%
rename from components/digital_asset_links/digital_asset_links_handler.cc
rename to components/content_relationship_verification/digital_asset_links_handler.cc
index a068326..b52de5c3 100644
--- a/components/digital_asset_links/digital_asset_links_handler.cc
+++ b/components/content_relationship_verification/digital_asset_links_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/digital_asset_links/digital_asset_links_handler.h"
+#include "components/content_relationship_verification/digital_asset_links_handler.h"
 
 #include <vector>
 
@@ -137,7 +137,7 @@
 
 }  // namespace
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 
 const char kDigitalAssetLinksCheckResponseKeyLinked[] = "linked";
 
@@ -158,8 +158,9 @@
     std::map<std::string, std::set<std::string>> target_values,
     std::unique_ptr<std::string> response_body) {
   int response_code = -1;
-  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
+  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers) {
     response_code = url_loader_->ResponseInfo()->headers->response_code();
+  }
 
   if (!response_body || response_code != net::HTTP_OK) {
     int net_error = url_loader_->NetError();
@@ -241,15 +242,17 @@
         break;
       }
     }
-    if (failed_target_check)
+    if (failed_target_check) {
       continue;
+    }
 
     std::move(callback_).Run(RelationshipCheckResult::kSuccess);
     return;
   }
 
-  for (const auto& failure_reason : failures)
+  for (const auto& failure_reason : failures) {
     AddMessageToConsole(web_contents_.get(), failure_reason);
+  }
 
   std::move(callback_).Run(RelationshipCheckResult::kFailure);
 }
@@ -283,8 +286,9 @@
     RelationshipCheckResultCallback callback) {
   GURL request_url = GetUrlForAssetLinks(web_domain);
 
-  if (!request_url.is_valid())
+  if (!request_url.is_valid()) {
     return false;
+  }
 
   // Resetting both the callback and SimpleURLLoader here to ensure
   // that any previous requests will never get a
@@ -338,4 +342,4 @@
   return true;
 }
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
diff --git a/components/digital_asset_links/digital_asset_links_handler.h b/components/content_relationship_verification/digital_asset_links_handler.h
similarity index 91%
rename from components/digital_asset_links/digital_asset_links_handler.h
rename to components/content_relationship_verification/digital_asset_links_handler.h
index 0175ab7..d3ae4bc 100644
--- a/components/digital_asset_links/digital_asset_links_handler.h
+++ b/components/content_relationship_verification/digital_asset_links_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DIGITAL_ASSET_LINKS_DIGITAL_ASSET_LINKS_HANDLER_H_
-#define COMPONENTS_DIGITAL_ASSET_LINKS_DIGITAL_ASSET_LINKS_HANDLER_H_
+#ifndef COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_DIGITAL_ASSET_LINKS_HANDLER_H_
+#define COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_DIGITAL_ASSET_LINKS_HANDLER_H_
 
 #include <map>
 #include <set>
@@ -27,11 +27,12 @@
 class Origin;
 }  // namespace url
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 
 extern const char kDigitalAssetLinksCheckResponseKeyLinked[];
 
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.digital_asset_links
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.components.content_relationship_verification)
 enum class RelationshipCheckResult {
   kSuccess = 0,
   kFailure,
@@ -127,6 +128,6 @@
   base::WeakPtrFactory<DigitalAssetLinksHandler> weak_ptr_factory_{this};
 };
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
 
-#endif  // COMPONENTS_DIGITAL_ASSET_LINKS_DIGITAL_ASSET_LINKS_HANDLER_H_
+#endif  // COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_DIGITAL_ASSET_LINKS_HANDLER_H_
diff --git a/components/digital_asset_links/digital_asset_links_handler_unittest.cc b/components/content_relationship_verification/digital_asset_links_handler_unittest.cc
similarity index 98%
rename from components/digital_asset_links/digital_asset_links_handler_unittest.cc
rename to components/content_relationship_verification/digital_asset_links_handler_unittest.cc
index 32564e58..52a3181f 100644
--- a/components/digital_asset_links/digital_asset_links_handler_unittest.cc
+++ b/components/content_relationship_verification/digital_asset_links_handler_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/digital_asset_links/digital_asset_links_handler.h"
+#include "components/content_relationship_verification/digital_asset_links_handler.h"
 
 #include "base/command_line.h"
 #include "base/functional/bind.h"
@@ -72,7 +72,7 @@
 
 }  // namespace
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 namespace {
 
 class DigitalAssetLinksHandlerTest : public ::testing::Test {
@@ -369,4 +369,4 @@
   EXPECT_EQ(result_, RelationshipCheckResult::kFailure);
 }
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
diff --git a/components/digital_asset_links/origin_verifier.cc b/components/content_relationship_verification/origin_verifier.cc
similarity index 86%
rename from components/digital_asset_links/origin_verifier.cc
rename to components/content_relationship_verification/origin_verifier.cc
index 8927741..16fd578 100644
--- a/components/digital_asset_links/origin_verifier.cc
+++ b/components/content_relationship_verification/origin_verifier.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/digital_asset_links/origin_verifier.h"
+#include "components/content_relationship_verification/origin_verifier.h"
 
 #include <memory>
 
@@ -10,8 +10,8 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/functional/bind.h"
-#include "components/digital_asset_links/android/jni_headers/OriginVerifier_jni.h"
-#include "components/digital_asset_links/digital_asset_links_handler.h"
+#include "components/content_relationship_verification/android/jni_headers/OriginVerifier_jni.h"
+#include "components/content_relationship_verification/digital_asset_links_handler.h"
 #include "content/public/browser/android/browser_context_handle.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -25,7 +25,7 @@
 using base::android::ConvertJavaStringToUTF16;
 using base::android::JavaParamRef;
 using base::android::JavaRef;
-using digital_asset_links::RelationshipCheckResult;
+using content_relationship_verification::RelationshipCheckResult;
 
 OriginVerifier::OriginVerifier(
     JNIEnv* env,
@@ -49,8 +49,9 @@
     const JavaParamRef<jstring>& j_origin,
     const JavaParamRef<jstring>& j_relationship,
     const base::android::JavaRef<jobject>& jweb_contents) {
-  if (!j_package_name || !j_fingerprints || !j_origin || !j_relationship)
+  if (!j_package_name || !j_fingerprints || !j_origin || !j_relationship) {
     return false;
+  }
   raw_ptr<content::WebContents> web_contents =
       content::WebContents::FromJavaWebContents(jweb_contents);
   std::string package_name = ConvertJavaStringToUTF8(env, j_package_name);
@@ -61,9 +62,9 @@
 
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  auto asset_link_handler =
-      std::make_unique<digital_asset_links::DigitalAssetLinksHandler>(
-          url_loader_factory_, web_contents);
+  auto asset_link_handler = std::make_unique<
+      content_relationship_verification::DigitalAssetLinksHandler>(
+      url_loader_factory_, web_contents);
 
   auto* asset_link_handler_ptr = asset_link_handler.get();
 
@@ -76,7 +77,8 @@
 }
 
 void OriginVerifier::OnRelationshipCheckComplete(
-    std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler> handler,
+    std::unique_ptr<content_relationship_verification::DigitalAssetLinksHandler>
+        handler,
     const std::string& origin,
     RelationshipCheckResult result) {
   JNIEnv* env = base::android::AttachCurrentThread();
diff --git a/components/digital_asset_links/origin_verifier.h b/components/content_relationship_verification/origin_verifier.h
similarity index 78%
rename from components/digital_asset_links/origin_verifier.h
rename to components/content_relationship_verification/origin_verifier.h
index b35e36e..ddfb0a9 100644
--- a/components/digital_asset_links/origin_verifier.h
+++ b/components/content_relationship_verification/origin_verifier.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DIGITAL_ASSET_LINKS_ORIGIN_VERIFIER_H_
-#define COMPONENTS_DIGITAL_ASSET_LINKS_ORIGIN_VERIFIER_H_
+#ifndef COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_ORIGIN_VERIFIER_H_
+#define COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_ORIGIN_VERIFIER_H_
 
 #include <memory>
 
@@ -13,10 +13,10 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 enum class RelationshipCheckResult;
 class DigitalAssetLinksHandler;
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
 
 // JNI bridge for OriginVerifier.java
 class OriginVerifier {
@@ -53,13 +53,14 @@
 
  private:
   void OnRelationshipCheckComplete(
-      std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler> handler,
+      std::unique_ptr<
+          content_relationship_verification::DigitalAssetLinksHandler> handler,
       const std::string& origin,
-      digital_asset_links::RelationshipCheckResult result);
+      content_relationship_verification::RelationshipCheckResult result);
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   base::android::ScopedJavaGlobalRef<jobject> jobject_;
 };
 
-#endif  // COMPONENTS_DIGITAL_ASSET_LINKS_ORIGIN_VERIFIER_H_
+#endif  // COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_ORIGIN_VERIFIER_H_
diff --git a/components/digital_asset_links/response_header_verifier.cc b/components/content_relationship_verification/response_header_verifier.cc
similarity index 87%
rename from components/digital_asset_links/response_header_verifier.cc
rename to components/content_relationship_verification/response_header_verifier.cc
index 3062c30..f22d6635 100644
--- a/components/digital_asset_links/response_header_verifier.cc
+++ b/components/content_relationship_verification/response_header_verifier.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/digital_asset_links/response_header_verifier.h"
+#include "components/content_relationship_verification/response_header_verifier.h"
 
 #include <stdio.h>
 
@@ -13,7 +13,7 @@
 const char kNormalizedHeaderDelimiter[] = ",";
 }  // namespace
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 
 const char kEmbedderAncestorHeader[] = "X-Embedder-Ancestors";
 
@@ -46,4 +46,4 @@
   return false;
 }
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
diff --git a/components/content_relationship_verification/response_header_verifier.h b/components/content_relationship_verification/response_header_verifier.h
new file mode 100644
index 0000000..b96ef34
--- /dev/null
+++ b/components/content_relationship_verification/response_header_verifier.h
@@ -0,0 +1,23 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_RESPONSE_HEADER_VERIFIER_H_
+#define COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_RESPONSE_HEADER_VERIFIER_H_
+
+#include <string>
+
+namespace content_relationship_verification {
+
+class ResponseHeaderVerifier {
+ public:
+  // Verify if the provided |package_name| is verified via the embedder
+  // ancestor header.
+  static bool Verify(const std::string& package_name,
+                     const std::string& embedder_ancestors_header_value);
+};
+
+extern const char kEmbedderAncestorHeader[];
+}  // namespace content_relationship_verification
+
+#endif  // COMPONENTS_CONTENT_RELATIONSHIP_VERIFICATION_RESPONSE_HEADER_VERIFIER_H_
diff --git a/components/digital_asset_links/response_header_verifier_unittest.cc b/components/content_relationship_verification/response_header_verifier_unittest.cc
similarity index 88%
rename from components/digital_asset_links/response_header_verifier_unittest.cc
rename to components/content_relationship_verification/response_header_verifier_unittest.cc
index 9f4f65e..8e62414a 100644
--- a/components/digital_asset_links/response_header_verifier_unittest.cc
+++ b/components/content_relationship_verification/response_header_verifier_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/digital_asset_links/response_header_verifier.h"
+#include "components/content_relationship_verification/response_header_verifier.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace digital_asset_links {
+namespace content_relationship_verification {
 
 TEST(ResponseHeaderVerifier, VerifyEmptyHeader) {
   EXPECT_TRUE(ResponseHeaderVerifier::Verify("any.package.name", ""));
@@ -39,4 +39,4 @@
       ResponseHeaderVerifier::Verify("another.package", "*, a.package"));
 }
 
-}  // namespace digital_asset_links
+}  // namespace content_relationship_verification
diff --git a/components/crash/content/browser/crash_handler_host_linux.cc b/components/crash/content/browser/crash_handler_host_linux.cc
index 47b44f24..d1f519f 100644
--- a/components/crash/content/browser/crash_handler_host_linux.cc
+++ b/components/crash/content/browser/crash_handler_host_linux.cc
@@ -444,7 +444,8 @@
   // Create a temporary file holding the AddressSanitizer report.
   const base::FilePath log_path =
       base::FilePath(minidump_filename).ReplaceExtension("log");
-  base::WriteFile(log_path, info->asan_report_str, info->asan_report_length);
+  base::WriteFile(log_path, base::StringPiece(info->asan_report_str,
+                                              info->asan_report_length));
 #endif
 
   // Freed in CrashDumpTask().
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/UrlResponseInfoTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/UrlResponseInfoTest.java
index 905633a1..40897cd69 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/UrlResponseInfoTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/UrlResponseInfoTest.java
@@ -46,6 +46,7 @@
                 new UrlResponseInfoImpl(urlChain, httpStatusCode, httpStatusText, allHeadersList,
                         wasCached, negotiatedProtocol, proxyServer, receivedByteCount);
         Assert.assertEquals(info.getUrlChain(), urlChain);
+        Assert.assertEquals(info.getUrl(), urlChain.get(urlChain.size() - 1));
         try {
             info.getUrlChain().add("example.com");
             Assert.fail("getUrlChain() returned modifyable list.");
diff --git a/components/desks_storage/core/desk_template_conversion.cc b/components/desks_storage/core/desk_template_conversion.cc
index 4738c86..f23c09e 100644
--- a/components/desks_storage/core/desk_template_conversion.cc
+++ b/components/desks_storage/core/desk_template_conversion.cc
@@ -171,50 +171,37 @@
 constexpr int kVersionNum = 1;
 
 // Conversion to desk methods.
-bool GetString(const base::Value* dict, const char* key, std::string* out) {
-  const base::Value* value =
-      dict->FindKeyOfType(key, base::Value::Type::STRING);
+bool GetString(const base::Value::Dict& dict,
+               const char* key,
+               std::string* out) {
+  const std::string* value = dict.FindString(key);
   if (!value)
     return false;
 
-  *out = value->GetString();
+  *out = *value;
   return true;
 }
 
-bool GetString(const base::Value& dict, const char* key, std::string* out) {
-  return GetString(&dict, key, out);
-}
-
-bool GetInt(const base::Value* dict, const char* key, int* out) {
-  const base::Value* value =
-      dict->FindKeyOfType(key, base::Value::Type::INTEGER);
+bool GetInt(const base::Value::Dict& dict, const char* key, int* out) {
+  absl::optional<int> value = dict.FindInt(key);
   if (!value)
     return false;
 
-  *out = value->GetInt();
+  *out = *value;
   return true;
 }
 
-bool GetInt(const base::Value& dict, const char* key, int* out) {
-  return GetInt(&dict, key, out);
-}
-
-bool GetBool(const base::Value* dict, const char* key, bool* out) {
-  const base::Value* value =
-      dict->FindKeyOfType(key, base::Value::Type::BOOLEAN);
+bool GetBool(const base::Value::Dict& dict, const char* key, bool* out) {
+  absl::optional<bool> value = dict.FindBool(key);
   if (!value)
     return false;
 
-  *out = value->GetBool();
+  *out = *value;
   return true;
 }
 
-bool GetBool(const base::Value& dict, const char* key, bool* out) {
-  return GetBool(&dict, key, out);
-}
-
 // Get App ID from App proto.
-std::string GetJsonAppId(const base::Value& app) {
+std::string GetJsonAppId(const base::Value::Dict& app) {
   std::string app_type;
   if (!GetString(app, kAppType, &app_type))
     return std::string();  // App Type must be specified.
@@ -247,20 +234,21 @@
   return std::string();
 }
 
-// Convert a TabGroupInfo object to a base::Value dictionary.
-base::Value ConvertTabGroupInfoToValue(
+// Convert a TabGroupInfo object to a base::Value::Dict.
+base::Value::Dict ConvertTabGroupInfoToDict(
     const tab_groups::TabGroupInfo& group_info) {
-  base::Value tab_group_dict(base::Value::Type::DICT);
+  base::Value::Dict tab_group_dict;
 
-  tab_group_dict.SetIntKey(kTabRangeFirstIndex, group_info.tab_range.start());
-  tab_group_dict.SetIntKey(kTabRangeLastIndex, group_info.tab_range.end());
-  tab_group_dict.SetStringKey(
-      kTabGroupTitleKey, base::UTF16ToUTF8(group_info.visual_data.title()));
-  tab_group_dict.SetStringKey(
-      kTabGroupColorKey,
-      tab_groups::TabGroupColorToString(group_info.visual_data.color()));
-  tab_group_dict.SetBoolKey(kTabGroupIsCollapsed,
-                            group_info.visual_data.is_collapsed());
+  tab_group_dict.Set(kTabRangeFirstIndex,
+                     static_cast<int>(group_info.tab_range.start()));
+  tab_group_dict.Set(kTabRangeLastIndex,
+                     static_cast<int>(group_info.tab_range.end()));
+  tab_group_dict.Set(kTabGroupTitleKey,
+                     base::UTF16ToUTF8(group_info.visual_data.title()));
+  tab_group_dict.Set(kTabGroupColorKey, tab_groups::TabGroupColorToString(
+                                            group_info.visual_data.color()));
+  tab_group_dict.Set(kTabGroupIsCollapsed,
+                     group_info.visual_data.is_collapsed());
 
   return tab_group_dict;
 }
@@ -301,8 +289,8 @@
 // Constructs a GroupVisualData from value `group_visual_data` IFF all fields
 // are present and valid in the value parameter.  Returns true on success, false
 // on failure.
-bool MakeTabGroupVisualDataFromValue(
-    const base::Value& tab_group,
+bool MakeTabGroupVisualDataFromDict(
+    const base::Value::Dict& tab_group,
     tab_groups::TabGroupVisualData* out_visual_data) {
   std::string tab_group_title;
   std::string group_color_string;
@@ -323,8 +311,8 @@
 // Constructs a gfx::Range from value `group_range` IFF all fields are
 // present and valid in the value parameter.  Returns true on success, false on
 // failure.
-bool MakeTabGroupRangeFromValue(const base::Value& tab_group,
-                                gfx::Range* out_range) {
+bool MakeTabGroupRangeFromDict(const base::Value::Dict& tab_group,
+                               gfx::Range* out_range) {
   int32_t range_start;
   int32_t range_end;
   if (GetInt(tab_group, kTabRangeFirstIndex, &range_start) &&
@@ -339,13 +327,13 @@
 // Constructs a TabGroupInfo from `tab_group` IFF all fields are present
 // and valid in the value parameter. Returns true on success, false on failure.
 absl::optional<tab_groups::TabGroupInfo> MakeTabGroupInfoFromDict(
-    const base::Value& tab_group) {
+    const base::Value::Dict& tab_group) {
   absl::optional<tab_groups::TabGroupInfo> tab_group_info = absl::nullopt;
 
   tab_groups::TabGroupVisualData visual_data;
   gfx::Range range;
-  if (MakeTabGroupRangeFromValue(tab_group, &range) &&
-      MakeTabGroupVisualDataFromValue(tab_group, &visual_data)) {
+  if (MakeTabGroupRangeFromDict(tab_group, &range) &&
+      MakeTabGroupVisualDataFromDict(tab_group, &visual_data)) {
     tab_group_info.emplace(range, visual_data);
   }
 
@@ -418,7 +406,7 @@
 
 // Convert App JSON to `app_restore::AppLaunchInfo`.
 std::unique_ptr<app_restore::AppLaunchInfo> ConvertJsonToAppLaunchInfo(
-    const base::Value& app) {
+    const base::Value::Dict& app) {
   int32_t window_id;
   if (!GetInt(app, kWindowId, &window_id))
     return nullptr;
@@ -480,11 +468,11 @@
 
     // Fill in the URL list
     app_launch_info->urls.emplace();
-    const base::Value* tabs = app.FindKeyOfType(kTabs, base::Value::Type::LIST);
+    const base::Value::List* tabs = app.FindList(kTabs);
     if (tabs) {
-      for (auto& tab : tabs->GetList()) {
+      for (auto& tab : *tabs) {
         std::string url;
-        if (GetString(tab, kTabUrl, &url)) {
+        if (GetString(tab.GetDict(), kTabUrl, &url)) {
           app_launch_info->urls.value().emplace_back(url);
         }
       }
@@ -492,12 +480,11 @@
 
     // Fill the tab groups
     app_launch_info->tab_group_infos.emplace();
-    const base::Value* tab_groups =
-        app.FindKeyOfType(kTabGroups, base::Value::Type::LIST);
+    const base::Value::List* tab_groups = app.FindList(kTabGroups);
     if (tab_groups) {
-      for (auto& tab : tab_groups->GetList()) {
+      for (auto& tab : *tab_groups) {
         absl::optional<tab_groups::TabGroupInfo> tab_group =
-            MakeTabGroupInfoFromDict(tab);
+            MakeTabGroupInfoFromDict(tab.GetDict());
         if (tab_group.has_value()) {
           app_launch_info->tab_group_infos->push_back(
               std::move(tab_group.value()));
@@ -561,38 +548,40 @@
 }
 
 void FillArcExtraWindowInfoFromJson(
-    const base::Value& app,
+    const base::Value::Dict& app,
     app_restore::WindowInfo::ArcExtraInfo* out_window_info) {
-  const base::Value* bounds_in_root = app.FindDictKey(kBoundsInRoot);
+  const base::Value::Dict* bounds_in_root = app.FindDict(kBoundsInRoot);
   int top;
   int left;
   int bounds_width;
   int bounds_height;
-  if (bounds_in_root && GetInt(bounds_in_root, kWindowBoundTop, &top) &&
-      GetInt(bounds_in_root, kWindowBoundLeft, &left) &&
-      GetInt(bounds_in_root, kWindowBoundWidth, &bounds_width) &&
-      GetInt(bounds_in_root, kWindowBoundHeight, &bounds_height))
+  if (bounds_in_root && GetInt(*bounds_in_root, kWindowBoundTop, &top) &&
+      GetInt(*bounds_in_root, kWindowBoundLeft, &left) &&
+      GetInt(*bounds_in_root, kWindowBoundWidth, &bounds_width) &&
+      GetInt(*bounds_in_root, kWindowBoundHeight, &bounds_height)) {
     out_window_info->bounds_in_root.emplace(left, top, bounds_width,
                                             bounds_height);
+  }
 
-  const base::Value* maximum_size = app.FindDictKey(kMaximumSize);
+  const base::Value::Dict* maximum_size = app.FindDict(kMaximumSize);
   int max_width;
   int max_height;
-  if (maximum_size && GetInt(maximum_size, kSizeWidth, &max_width) &&
-      GetInt(maximum_size, kSizeHeight, &max_height))
+  if (maximum_size && GetInt(*maximum_size, kSizeWidth, &max_width) &&
+      GetInt(*maximum_size, kSizeHeight, &max_height)) {
     out_window_info->maximum_size.emplace(max_width, max_height);
+  }
 
-  const base::Value* minimum_size = app.FindDictKey(kMinimumSize);
+  const base::Value::Dict* minimum_size = app.FindDict(kMinimumSize);
   int min_width;
   int min_height;
-  if (minimum_size && GetInt(minimum_size, kSizeWidth, &min_width) &&
-      GetInt(minimum_size, kSizeHeight, &min_height)) {
+  if (minimum_size && GetInt(*minimum_size, kSizeWidth, &min_width) &&
+      GetInt(*minimum_size, kSizeHeight, &min_height)) {
     out_window_info->minimum_size.emplace(min_width, min_height);
   }
 }
 
 // Fill `out_window_info` with information from JSON `app`.
-void FillWindowInfoFromJson(const base::Value& app,
+void FillWindowInfoFromJson(const base::Value::Dict& app,
                             app_restore::WindowInfo* out_window_info) {
   std::string window_state;
   chromeos::WindowStateType cros_window_state =
@@ -609,15 +598,15 @@
                                    &out_window_info->arc_extra_info.emplace());
   }
 
-  const base::Value* window_bound = app.FindDictKey(kWindowBound);
+  const base::Value::Dict* window_bound = app.FindDict(kWindowBound);
   int top;
   int left;
   int width;
   int height;
-  if (window_bound && GetInt(window_bound, kWindowBoundTop, &top) &&
-      GetInt(window_bound, kWindowBoundLeft, &left) &&
-      GetInt(window_bound, kWindowBoundWidth, &width) &&
-      GetInt(window_bound, kWindowBoundHeight, &height)) {
+  if (window_bound && GetInt(*window_bound, kWindowBoundTop, &top) &&
+      GetInt(*window_bound, kWindowBoundLeft, &left) &&
+      GetInt(*window_bound, kWindowBoundWidth, &width) &&
+      GetInt(*window_bound, kWindowBoundHeight, &height)) {
     out_window_info->current_bounds.emplace(left, top, width, height);
   }
 
@@ -658,20 +647,22 @@
   const base::Value* apps = desk->FindListKey(kApps);
   if (apps) {
     for (const auto& app : apps->GetList()) {
+      const base::Value::Dict& app_dict = app.GetDict();
       std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info =
-          ConvertJsonToAppLaunchInfo(app);
+          ConvertJsonToAppLaunchInfo(app_dict);
       if (!app_launch_info)
         continue;  // Skip unsupported app.
 
       int window_id;
-      if (!GetInt(app, kWindowId, &window_id))
+      if (!GetInt(app_dict, kWindowId, &window_id)) {
         return nullptr;
+      }
 
       const std::string app_id = app_launch_info->app_id;
       restore_data->AddAppLaunchInfo(std::move(app_launch_info));
 
       app_restore::WindowInfo app_window_info;
-      FillWindowInfoFromJson(app, &app_window_info);
+      FillWindowInfoFromJson(app_dict, &app_window_info);
 
       restore_data->ModifyWindowInfo(app_id, window_id, app_window_info);
     }
@@ -905,7 +896,7 @@
     base::Value::List tab_groups_value;
 
     for (const auto& tab_group : app->tab_group_infos.value()) {
-      tab_groups_value.Append(ConvertTabGroupInfoToValue(tab_group));
+      tab_groups_value.Append(ConvertTabGroupInfoToDict(tab_group));
     }
 
     app_data.Set(kTabGroups, std::move(tab_groups_value));
@@ -1941,6 +1932,8 @@
   if (!policy_json.is_dict())
     return nullptr;
 
+  const base::Value::Dict& policy_json_dict = policy_json.GetDict();
+
   int version;
   std::string uuid_str;
   std::string name;
@@ -1949,16 +1942,17 @@
   int64_t created_time_usec;
   int64_t updated_time_usec;
   const base::Value* desk = policy_json.FindDictKey(kDesk);
-  if (!desk || !GetInt(policy_json, kVersion, &version) ||
-      !GetString(policy_json, kUuid, &uuid_str) ||
-      !GetString(policy_json, kName, &name) ||
-      !GetString(policy_json, kCreatedTime, &created_time_usec_str) ||
+  if (!desk || !GetInt(policy_json_dict, kVersion, &version) ||
+      !GetString(policy_json_dict, kUuid, &uuid_str) ||
+      !GetString(policy_json_dict, kName, &name) ||
+      !GetString(policy_json_dict, kCreatedTime, &created_time_usec_str) ||
       !base::StringToInt64(created_time_usec_str, &created_time_usec) ||
-      !GetString(policy_json, kUpdatedTime, &updated_time_usec_str) ||
+      !GetString(policy_json_dict, kUpdatedTime, &updated_time_usec_str) ||
       !base::StringToInt64(updated_time_usec_str, &updated_time_usec) ||
       name.empty() || created_time_usec_str.empty() ||
-      updated_time_usec_str.empty())
+      updated_time_usec_str.empty()) {
     return nullptr;
+  }
 
   base::GUID uuid = base::GUID::ParseCaseInsensitive(uuid_str);
   if (!uuid.is_valid())
@@ -1966,7 +1960,7 @@
 
   // Set default value for the desk type to template.
   std::string desk_type_string;
-  if (!GetString(policy_json, kDeskType, &desk_type_string)) {
+  if (!GetString(policy_json_dict, kDeskType, &desk_type_string)) {
     desk_type_string = kDeskTypeTemplate;
   } else if (!IsValidDeskTemplateType(desk_type_string)) {
     return nullptr;
@@ -1988,19 +1982,18 @@
 
 base::Value SerializeDeskTemplateAsPolicy(const ash::DeskTemplate* desk,
                                           apps::AppRegistryCache* app_cache) {
-  base::Value desk_dict(base::Value::Type::DICT);
-  desk_dict.SetKey(kVersion, base::Value(kVersionNum));
-  desk_dict.SetKey(kUuid, base::Value(desk->uuid().AsLowercaseString()));
-  desk_dict.SetKey(kName, base::Value(desk->template_name()));
-  desk_dict.SetKey(kCreatedTime, base::TimeToValue(desk->created_time()));
-  desk_dict.SetKey(kUpdatedTime, base::TimeToValue(desk->GetLastUpdatedTime()));
-  desk_dict.SetKey(kDeskType,
-                   base::Value(SerializeDeskTypeAsString(desk->type())));
+  base::Value::Dict desk_dict;
+  desk_dict.Set(kVersion, kVersionNum);
+  desk_dict.Set(kUuid, desk->uuid().AsLowercaseString());
+  desk_dict.Set(kName, desk->template_name());
+  desk_dict.Set(kCreatedTime, base::TimeToValue(desk->created_time()));
+  desk_dict.Set(kUpdatedTime, base::TimeToValue(desk->GetLastUpdatedTime()));
+  desk_dict.Set(kDeskType, SerializeDeskTypeAsString(desk->type()));
 
-  desk_dict.SetKey(
+  desk_dict.Set(
       kDesk, ConvertRestoreDataToValue(desk->desk_restore_data(), app_cache));
 
-  return desk_dict;
+  return base::Value(std::move(desk_dict));
 }
 
 std::unique_ptr<DeskTemplate> FromSyncProto(
diff --git a/components/digital_asset_links/README.md b/components/digital_asset_links/README.md
deleted file mode 100644
index d619dd6..0000000
--- a/components/digital_asset_links/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Digital asset links
-
-This component contains utilities to facilitate making requests of the
-DigitalAssetLinks API on the web.
-
-See [documentation](https://developers.google.com/digital-asset-links/v1/getting-started).
diff --git a/components/digital_asset_links/android/BUILD.gn b/components/digital_asset_links/android/BUILD.gn
deleted file mode 100644
index d29572a..0000000
--- a/components/digital_asset_links/android/BUILD.gn
+++ /dev/null
@@ -1,41 +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.
-
-import("//build/config/android/rules.gni")
-
-android_library("java") {
-  sources = [
-    "java/src/org/chromium/components/digital_asset_links/OriginVerificationScheduler.java",
-    "java/src/org/chromium/components/digital_asset_links/OriginVerifier.java",
-    "java/src/org/chromium/components/digital_asset_links/OriginVerifierHelper.java",
-    "java/src/org/chromium/components/digital_asset_links/Relationship.java",
-    "java/src/org/chromium/components/digital_asset_links/VerificationResultStore.java",
-  ]
-  deps = [
-    "//base:base_java",
-    "//base:jni_java",
-    "//build/android:build_java",
-    "//components/digital_asset_links:java",
-    "//components/embedder_support/android:util_java",
-    "//content/public/android:content_java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-  ]
-  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
-}
-
-robolectric_library("junit_test_support") {
-  sources = [ "java/src/org/chromium/components/digital_asset_links/OriginVerifierUnitTestSupport.java" ]
-  deps = [
-    ":java",
-    "//components/embedder_support/android:util_java",
-    "//third_party/androidx:androidx_browser_browser_java",
-    "//third_party/mockito:mockito_java",
-  ]
-}
-
-generate_jni("jni_headers") {
-  sources = [
-    "java/src/org/chromium/components/digital_asset_links/OriginVerifier.java",
-  ]
-}
diff --git a/components/digital_asset_links/digital_asset_links_constants.cc b/components/digital_asset_links/digital_asset_links_constants.cc
deleted file mode 100644
index 3ef25ab..0000000
--- a/components/digital_asset_links/digital_asset_links_constants.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/digital_asset_links/digital_asset_links_constants.h"
-
-#include "net/base/net_errors.h"
-
-namespace digital_asset_links {
-
-const char kCustomCancelReasonForURLLoader[] = "DigitalAssetLinks";
-
-const int kNetErrorCodeForDigitalAssetLinks = net::ERR_ACCESS_DENIED;
-
-}  // namespace digital_asset_links
diff --git a/components/digital_asset_links/digital_asset_links_constants.h b/components/digital_asset_links/digital_asset_links_constants.h
deleted file mode 100644
index 5c34f0e..0000000
--- a/components/digital_asset_links/digital_asset_links_constants.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_DIGITAL_ASSET_LINKS_DIGITAL_ASSET_LINKS_CONSTANTS_H_
-#define COMPONENTS_DIGITAL_ASSET_LINKS_DIGITAL_ASSET_LINKS_CONSTANTS_H_
-
-namespace digital_asset_links {
-
-// When a network::mojom::URLLoader is cancelled because of digital asset link
-// verification, this custom cancellation reason could be used to notify the
-// implementation side. Please see
-// network::mojom::URLLoader::kClientDisconnectReason for more details.
-extern const char kCustomCancelReasonForURLLoader[];
-
-// error_code to use when Safe Browsing blocks a request.
-extern const int kNetErrorCodeForDigitalAssetLinks;
-
-}  // namespace digital_asset_links
-
-#endif  // COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_CONSTANTS_H_
diff --git a/components/digital_asset_links/response_header_verifier.h b/components/digital_asset_links/response_header_verifier.h
deleted file mode 100644
index dd1c98a..0000000
--- a/components/digital_asset_links/response_header_verifier.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_DIGITAL_ASSET_LINKS_RESPONSE_HEADER_VERIFIER_H_
-#define COMPONENTS_DIGITAL_ASSET_LINKS_RESPONSE_HEADER_VERIFIER_H_
-
-#include <string>
-
-namespace digital_asset_links {
-
-class ResponseHeaderVerifier {
- public:
-  // Verify if the provided |package_name| is verified via the embedder
-  // ancestor header.
-  static bool Verify(const std::string& package_name,
-                     const std::string& embedder_ancestors_header_value);
-};
-
-extern const char kEmbedderAncestorHeader[];
-}  // namespace digital_asset_links
-
-#endif  // COMPONENTS_DIGITAL_ASSET_LINKS_RESPONSE_HEADER_VERIFIER_H_
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index 9a36fce..95850270 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -540,6 +540,12 @@
     UpdateShadow();
   }
 
+  if (root_surface() && window_state->GetStateType() != old_type &&
+      (window_state->GetStateType() == chromeos::WindowStateType::kFullscreen ||
+       old_type == chromeos::WindowStateType::kFullscreen)) {
+    root_surface()->OnFullscreenStateChanged(window_state->IsFullscreen());
+  }
+
   // Re-enable animations if they were disabled in pre state change handler.
   animations_disabler_.reset();
   window_state_is_changing_ = false;
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index c6dfeae..814080e 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -1540,6 +1540,7 @@
     SetPip();
 
   root_surface()->OnDeskChanged(GetWindowDeskStateChanged(window));
+  root_surface()->OnFullscreenStateChanged(widget_->IsFullscreen());
 
   WMHelper::GetInstance()->NotifyExoWindowCreated(widget_->GetNativeWindow());
 }
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 514c5b12..673fcd1 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -999,7 +999,6 @@
         cached_state_.buffer.reset();
       }
       state_.rounded_corners_bounds = cached_state_.rounded_corners_bounds;
-      state_.overlay_priority_hint = cached_state_.overlay_priority_hint;
       state_.clip_rect = cached_state_.clip_rect;
       state_.surface_transform = cached_state_.surface_transform;
       state_.acquire_fence = std::move(cached_state_.acquire_fence);
@@ -1008,6 +1007,12 @@
       if (state_.basic_state.alpha)
         needs_update_resource_ = true;
     }
+
+    // The overlay priority hint can get set before any buffer gets
+    // allocated/attached and may influence the format/modifier selection for
+    // these.
+    UpdateOverlayPriorityHint(cached_state_.overlay_priority_hint);
+
     // Either we didn't have a pending acquire fence, or we had one along with
     // a new buffer, and it was already moved to state_.acquire_fence. Note that
     // it is a commit-time client error to commit a fence without a buffer.
@@ -1343,6 +1348,17 @@
   }
 }
 
+void Surface::UpdateOverlayPriorityHint(OverlayPriority overlay_priority_hint) {
+  if (state_.overlay_priority_hint == overlay_priority_hint) {
+    return;
+  }
+
+  state_.overlay_priority_hint = overlay_priority_hint;
+  for (SurfaceObserver& observer : observers_) {
+    observer.OnOverlayPriorityHintChanged(overlay_priority_hint);
+  }
+}
+
 // Try to share the |SharedQuadState| (sqs) when a single layer can be
 // reconstructed. This is important for performance reasons in the occlusion
 // code and correctness in the per edge anti-alias code.
@@ -1796,4 +1812,13 @@
   }
 }
 
+void Surface::OnFullscreenStateChanged(bool fullscreen) {
+  for (SurfaceObserver& observer : observers_) {
+    observer.OnFullscreenStateChanged(fullscreen);
+  }
+  for (const auto& [surface, point] : sub_surfaces_) {
+    surface->OnFullscreenStateChanged(fullscreen);
+  }
+}
+
 }  // namespace exo
diff --git a/components/exo/surface.h b/components/exo/surface.h
index a670aa9..e77bbdf3 100644
--- a/components/exo/surface.h
+++ b/components/exo/surface.h
@@ -454,6 +454,13 @@
   // A negative number removes it.
   void SetClientAccessibilityId(int id);
 
+  // Inform observers and subsurfaces about new fullscreen state
+  void OnFullscreenStateChanged(bool fullscreen);
+
+  OverlayPriority GetOverlayPriorityHint() {
+    return state_.overlay_priority_hint;
+  }
+
  private:
   struct State {
     State();
@@ -575,6 +582,9 @@
   // Updates buffer_transform_ to match the current buffer parameters.
   void UpdateBufferTransform(bool y_invert);
 
+  // Update state_.overlay_priority_hint and notify observers
+  void UpdateOverlayPriorityHint(OverlayPriority overlay_priority_hint);
+
   // Puts the current surface into a draw quad, and appends the draw quads into
   // the |frame|.
   void AppendContentsToFrame(const gfx::PointF& origin,
diff --git a/components/exo/surface_observer.h b/components/exo/surface_observer.h
index 5121d6f..e9459b3 100644
--- a/components/exo/surface_observer.h
+++ b/components/exo/surface_observer.h
@@ -14,6 +14,7 @@
 
 namespace exo {
 class Surface;
+enum class OverlayPriority;
 
 // Observers can listen to various events on the Surfaces.
 class SurfaceObserver {
@@ -59,6 +60,11 @@
   // Called when tooltip is hidden.
   virtual void OnTooltipHidden(Surface* surface) {}
 
+  virtual void OnFullscreenStateChanged(bool fullscreen) {}
+
+  virtual void OnOverlayPriorityHintChanged(
+      OverlayPriority overlay_priority_hint) {}
+
  protected:
   virtual ~SurfaceObserver() {}
 };
diff --git a/components/exo/wayland/wayland_dmabuf_feedback_manager.cc b/components/exo/wayland/wayland_dmabuf_feedback_manager.cc
index 9aaa8ef0..f9335fd 100644
--- a/components/exo/wayland/wayland_dmabuf_feedback_manager.cc
+++ b/components/exo/wayland/wayland_dmabuf_feedback_manager.cc
@@ -14,6 +14,9 @@
 #include "base/functional/bind.h"
 #include "components/exo/buffer.h"
 #include "components/exo/display.h"
+#include "components/exo/shell_surface_base.h"
+#include "components/exo/shell_surface_util.h"
+#include "components/exo/surface.h"
 #include "components/exo/wayland/server_util.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "ui/aura/env.h"
@@ -195,6 +198,8 @@
   ~WaylandDmabufSurfaceFeedback() override;
 
   void OnSurfaceDestroying(Surface* surface) override {
+    feedback_manager_->RemoveSurfaceFromScanoutCandidates(
+        surface_, ScanoutReasonFlags::kNone);
     feedback_manager_->RemoveSurfaceFeedback(surface_);
   }
 
@@ -204,6 +209,27 @@
     feedback_manager_->MaybeResendFeedback(surface_);
   }
 
+  void OnFullscreenStateChanged(bool fullscreen) override {
+    if (fullscreen) {
+      feedback_manager_->AddSurfaceToScanoutCandidates(
+          surface_, ScanoutReasonFlags::kFullscreen);
+    } else {
+      feedback_manager_->RemoveSurfaceFromScanoutCandidates(
+          surface_, ScanoutReasonFlags::kFullscreen);
+    }
+  }
+
+  void OnOverlayPriorityHintChanged(
+      OverlayPriority overlay_priority_hint) override {
+    if (overlay_priority_hint == OverlayPriority::REQUIRED) {
+      feedback_manager_->AddSurfaceToScanoutCandidates(
+          surface_, ScanoutReasonFlags::kOverlayPriorityHint);
+    } else {
+      feedback_manager_->RemoveSurfaceFromScanoutCandidates(
+          surface_, ScanoutReasonFlags::kOverlayPriorityHint);
+    }
+  }
+
   void AddSurfaceFeedbackRef(
       WaylandDmabufSurfaceFeedbackResourceWrapper* surface_feedback_ref) {
     surface_feedback_refs_.insert(surface_feedback_ref);
@@ -402,6 +428,20 @@
         std::make_unique<WaylandDmabufFeedback>(default_feedback_);
     auto new_surface_feedback = std::make_unique<WaylandDmabufSurfaceFeedback>(
         this, surface, std::move(defaut_feedback_copy));
+
+    ShellSurfaceBase* shell_surface_base = nullptr;
+    for (auto* window = surface->window(); window && !shell_surface_base;
+         window = window->parent()) {
+      shell_surface_base = GetShellSurfaceBaseForWindow(window);
+    }
+    if (shell_surface_base) {
+      bool fullscreen = shell_surface_base->GetWidget()->IsFullscreen();
+      new_surface_feedback->OnFullscreenStateChanged(fullscreen);
+    }
+
+    new_surface_feedback->OnOverlayPriorityHintChanged(
+        surface->GetOverlayPriorityHint());
+
     it = surface_feedbacks_.emplace_hint(it, surface,
                                          std::move(new_surface_feedback));
   }
@@ -431,23 +471,31 @@
 }
 
 void WaylandDmabufFeedbackManager::AddSurfaceToScanoutCandidates(
-    Surface* surface) {
-  if (base::Contains(scanout_candidates_, surface))
+    Surface* surface,
+    ScanoutReasonFlags reason) {
+  auto search = scanout_candidates_.find(surface);
+  if (search != scanout_candidates_.end()) {
+    search->second = static_cast<ScanoutReasonFlags>(
+        static_cast<uint32_t>(search->second) | static_cast<uint32_t>(reason));
     return;
+  }
 
-  scanout_candidates_.emplace(surface);
+  scanout_candidates_.emplace(surface, reason);
 
-  if (!base::Contains(surface_feedbacks_, surface))
+  if (!base::Contains(surface_feedbacks_, surface)) {
     return;
+  }
 
   const auto& surface_feedback = surface_feedbacks_[surface];
   auto* feedback = surface_feedback->GetFeedback();
-  if (feedback->GetScanoutTranche())
+  if (feedback->GetScanoutTranche()) {
     return;
+  }
 
   feedback->MaybeAddScanoutTranche(surface);
-  if (!feedback->GetScanoutTranche())
+  if (!feedback->GetScanoutTranche()) {
     return;
+  }
 
   for (auto* feedback_ref : surface_feedback->GetFeedbackRefs()) {
     SendFeedback(feedback, feedback_ref->GetFeedbackResource());
@@ -455,19 +503,31 @@
 }
 
 void WaylandDmabufFeedbackManager::RemoveSurfaceFromScanoutCandidates(
-    Surface* surface) {
-  if (!base::Contains(scanout_candidates_, surface))
+    Surface* surface,
+    ScanoutReasonFlags reason) {
+  auto search = scanout_candidates_.find(surface);
+  if (search == scanout_candidates_.end()) {
     return;
+  }
 
-  scanout_candidates_.erase(surface);
-
-  if (!base::Contains(surface_feedbacks_, surface))
+  search->second = static_cast<ScanoutReasonFlags>(
+      static_cast<uint32_t>(search->second) & ~static_cast<uint32_t>(reason));
+  if (search->second == ScanoutReasonFlags::kNone ||
+      reason == ScanoutReasonFlags::kNone) {
+    scanout_candidates_.erase(surface);
+  } else {
     return;
+  }
+
+  if (!base::Contains(surface_feedbacks_, surface)) {
+    return;
+  }
 
   const auto& surface_feedback = surface_feedbacks_[surface];
   auto* feedback = surface_feedback->GetFeedback();
-  if (!feedback->GetScanoutTranche())
+  if (!feedback->GetScanoutTranche()) {
     return;
+  }
 
   feedback->ClearScanoutTranche();
   for (auto* feedback_ref : surface_feedback->GetFeedbackRefs()) {
diff --git a/components/exo/wayland/wayland_dmabuf_feedback_manager.h b/components/exo/wayland/wayland_dmabuf_feedback_manager.h
index 741ff051..26145cb9 100644
--- a/components/exo/wayland/wayland_dmabuf_feedback_manager.h
+++ b/components/exo/wayland/wayland_dmabuf_feedback_manager.h
@@ -7,8 +7,8 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <map>
 #include <memory>
-#include <set>
 
 #include "base/containers/flat_map.h"
 #include "base/memory/read_only_shared_memory_region.h"
@@ -32,6 +32,12 @@
 using IndexedDrmFormatsAndModifiers =
     base::flat_map<uint32_t, base::flat_map<size_t, uint64_t>>;
 
+enum class ScanoutReasonFlags : uint32_t {
+  kNone = 0,
+  kFullscreen = 1,
+  kOverlayPriorityHint = 2
+};
+
 class WaylandDmabufFeedbackManager {
  public:
   explicit WaylandDmabufFeedbackManager(Display* display);
@@ -56,8 +62,10 @@
                           wl_resource* surface_resource);
   void RemoveSurfaceFeedback(Surface* surface);
 
-  void AddSurfaceToScanoutCandidates(Surface* surface);
-  void RemoveSurfaceFromScanoutCandidates(Surface* surface);
+  void AddSurfaceToScanoutCandidates(Surface* surface,
+                                     ScanoutReasonFlags reason);
+  void RemoveSurfaceFromScanoutCandidates(Surface* surface,
+                                          ScanoutReasonFlags reason);
   void MaybeResendFeedback(Surface* surface);
 
  private:
@@ -72,7 +80,7 @@
   std::unique_ptr<WaylandDmabufFeedback> default_feedback_;
   base::flat_map<Surface*, std::unique_ptr<WaylandDmabufSurfaceFeedback>>
       surface_feedbacks_;
-  std::set<Surface*> scanout_candidates_;
+  std::map<Surface*, ScanoutReasonFlags> scanout_candidates_;
 };
 
 }  // namespace wayland
diff --git a/components/feed/core/v2/BUILD.gn b/components/feed/core/v2/BUILD.gn
index b1d3fb4..8d372c1 100644
--- a/components/feed/core/v2/BUILD.gn
+++ b/components/feed/core/v2/BUILD.gn
@@ -71,6 +71,7 @@
     "public/public_types.cc",
     "public/stream_type.cc",
     "public/unread_content_observer.cc",
+    "public/web_feed_subscriptions.cc",
     "request_throttler.cc",
     "request_throttler.h",
     "scheduling.cc",
diff --git a/components/feed/core/v2/public/test/stub_web_feed_subscriptions.h b/components/feed/core/v2/public/test/stub_web_feed_subscriptions.h
index 3b51199..e208cf9f 100644
--- a/components/feed/core/v2/public/test/stub_web_feed_subscriptions.h
+++ b/components/feed/core/v2/public/test/stub_web_feed_subscriptions.h
@@ -45,6 +45,9 @@
   void QueryWebFeed(
       const GURL& gurl,
       base::OnceCallback<void(QueryWebFeedResult)> callback) override {}
+  void QueryWebFeedId(
+      const std::string& web_feed_id,
+      base::OnceCallback<void(QueryWebFeedResult)> callback) override {}
 };
 
 }  // namespace feed
diff --git a/components/feed/core/v2/public/web_feed_subscriptions.cc b/components/feed/core/v2/public/web_feed_subscriptions.cc
new file mode 100644
index 0000000..e625b4e
--- /dev/null
+++ b/components/feed/core/v2/public/web_feed_subscriptions.cc
@@ -0,0 +1,14 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/feed/core/v2/public/web_feed_subscriptions.h"
+
+namespace feed {
+
+WebFeedSubscriptions::QueryWebFeedResult::QueryWebFeedResult() = default;
+WebFeedSubscriptions::QueryWebFeedResult::QueryWebFeedResult(
+    const QueryWebFeedResult& query_web_feed_result) = default;
+WebFeedSubscriptions::QueryWebFeedResult::~QueryWebFeedResult() = default;
+
+}  // namespace feed
\ No newline at end of file
diff --git a/components/feed/core/v2/public/web_feed_subscriptions.h b/components/feed/core/v2/public/web_feed_subscriptions.h
index cd6fc47f..a140869 100644
--- a/components/feed/core/v2/public/web_feed_subscriptions.h
+++ b/components/feed/core/v2/public/web_feed_subscriptions.h
@@ -28,10 +28,17 @@
     feedwire::webfeed::WebFeedChangeReason change_reason;
   };
   struct QueryWebFeedResult {
+    QueryWebFeedResult();
+    QueryWebFeedResult(const QueryWebFeedResult& query_web_feed_result);
+    ~QueryWebFeedResult();
     WebFeedQueryRequestStatus request_status =
         WebFeedQueryRequestStatus::kUnknown;
     // The id of the queried web feed.
     std::string web_feed_id;
+    // The title of the queried web feed.
+    std::string title;
+    // The url of the queried web feed.
+    std::string url;
   };
   // Follow a web feed given information about a web page. Calls `callback` when
   // complete. The callback parameter reports whether the url is now considered
@@ -118,6 +125,11 @@
       const GURL& url,
       base::OnceCallback<void(QueryWebFeedResult)> callback) = 0;
 
+  // return the WebFeed Id based on the id provided.
+  virtual void QueryWebFeedId(
+      const std::string& web_feed_id,
+      base::OnceCallback<void(QueryWebFeedResult)> callback) = 0;
+
   // Output debugging information for snippets-internals.
   virtual void DumpStateForDebugging(std::ostream& ss) {}
 };
diff --git a/components/feed/core/v2/web_feed_subscription_coordinator.cc b/components/feed/core/v2/web_feed_subscription_coordinator.cc
index 80df642c..2a31e7a 100644
--- a/components/feed/core/v2/web_feed_subscription_coordinator.cc
+++ b/components/feed/core/v2/web_feed_subscription_coordinator.cc
@@ -985,11 +985,30 @@
                          base::Unretained(this), std::move(callback))));
 }
 
+void WebFeedSubscriptionCoordinator::QueryWebFeedId(
+    const std::string& id,
+    base::OnceCallback<void(QueryWebFeedResult)> callback) {
+  // TODO(crbug/1409701) Combine subscription status into result callback. This
+  // would require binding a start call via WithModel and updating the local
+  // state to match the result from the server,
+  QueryWebFeedTask::Request request;
+  request.web_feed_id = id;
+
+  feed_stream_->GetTaskQueue().AddTask(
+      FROM_HERE,
+      std::make_unique<QueryWebFeedTask>(
+          feed_stream_, token_generator_.Token(), std::move(request),
+          base::BindOnce(&WebFeedSubscriptionCoordinator::QueryWebFeedComplete,
+                         base::Unretained(this), std::move(callback))));
+}
+
 void WebFeedSubscriptionCoordinator::QueryWebFeedComplete(
     base::OnceCallback<void(QueryWebFeedResult)> callback,
     QueryWebFeedResult result) {
   QueryWebFeedResult callback_result;
   callback_result.web_feed_id = result.web_feed_id;
+  callback_result.url = result.url;
+  callback_result.title = result.title;
   callback_result.request_status = result.request_status;
   feed_stream_->GetMetricsReporter().OnQueryAttempt(callback_result);
   std::move(callback).Run(std::move(callback_result));
diff --git a/components/feed/core/v2/web_feed_subscription_coordinator.h b/components/feed/core/v2/web_feed_subscription_coordinator.h
index e1f1031..dada996d 100644
--- a/components/feed/core/v2/web_feed_subscription_coordinator.h
+++ b/components/feed/core/v2/web_feed_subscription_coordinator.h
@@ -84,6 +84,9 @@
   void QueryWebFeed(
       const GURL& url,
       base::OnceCallback<void(QueryWebFeedResult)> callback) override;
+  void QueryWebFeedId(
+      const std::string& web_feed_id,
+      base::OnceCallback<void(QueryWebFeedResult)> callback) override;
 
   // Types / functions exposed for task implementations.
 
diff --git a/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.cc b/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.cc
index 1b82a51b..e650538 100644
--- a/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.cc
+++ b/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.cc
@@ -27,6 +27,7 @@
       request_(std::move(request)),
       callback_(std::move(callback)) {
   url_ = request_.web_feed_url;
+  web_feed_id_ = request_.web_feed_id;
 }
 
 QueryWebFeedTask::~QueryWebFeedTask() = default;
@@ -47,8 +48,11 @@
 
   feedwire::webfeed::QueryWebFeedRequest request;
   SetConsistencyToken(request, stream_->GetMetadata().consistency_token());
-  request.mutable_web_feed_uris()->set_web_page_uri(url_.spec());
-
+  if (!web_feed_id_.empty()) {
+    request.set_name(web_feed_id_);
+  } else {
+    request.mutable_web_feed_uris()->set_web_page_uri(url_.spec());
+  }
   stream_->GetNetwork().SendApiRequest<QueryWebFeedDiscoverApi>(
       request, stream_->GetAccountInfo(), stream_->GetSignedInRequestMetadata(),
       base::BindOnce(&QueryWebFeedTask::RequestComplete,
@@ -76,6 +80,8 @@
   WebFeedSubscriptions::QueryWebFeedResult result;
   result.request_status = status;
   result.web_feed_id = queried_web_feed_info_.web_feed_id();
+  result.title = queried_web_feed_info_.title();
+  result.url = queried_web_feed_info_.visit_uri();
   std::move(callback_).Run(std::move(result));
   TaskComplete();
 }
diff --git a/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.h b/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.h
index 5f484ef..fc20263 100644
--- a/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.h
+++ b/components/feed/core/v2/web_feed_subscriptions/query_web_feed_task.h
@@ -25,6 +25,7 @@
  public:
   struct Request {
     GURL web_feed_url;
+    std::string web_feed_id;
   };
   QueryWebFeedTask(
       FeedStream* stream,
@@ -51,6 +52,7 @@
 
   WebFeedSubscriptions::QueryWebFeedResult result_;
   GURL url_;
+  std::string web_feed_id_;
 };
 }  // namespace feed
 
diff --git a/components/feedback/feedback_util.cc b/components/feedback/feedback_util.cc
index 22e0d2cb..0cc7224 100644
--- a/components/feedback/feedback_util.cc
+++ b/components/feedback/feedback_util.cc
@@ -35,8 +35,7 @@
   // another temporary file to receive the zip file in.
   if (!temp_dir.CreateUniqueTempDir())
     return false;
-  if (base::WriteFile(temp_dir.GetPath().Append(filename), data.c_str(),
-                      data.size()) == -1) {
+  if (!base::WriteFile(temp_dir.GetPath().Append(filename), data)) {
     return false;
   }
 
diff --git a/components/installedapp/android/BUILD.gn b/components/installedapp/android/BUILD.gn
index e5212eb3c..852ad7c 100644
--- a/components/installedapp/android/BUILD.gn
+++ b/components/installedapp/android/BUILD.gn
@@ -8,7 +8,7 @@
   sources = [ "installed_app_verifier.cc" ]
   deps = [
     "//base",
-    "//components/digital_asset_links",
+    "//components/content_relationship_verification",
     "//content/public/browser",
   ]
   public_deps = [ ":jni_headers" ]
diff --git a/components/installedapp/android/DEPS b/components/installedapp/android/DEPS
index 894166b..28ce449 100644
--- a/components/installedapp/android/DEPS
+++ b/components/installedapp/android/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
   "+base",
-  "+components/digital_asset_links",
+  "+components/content_relationship_verification",
   "+components/embedder_support/android/browser_context",
   "+components/embedder_support/android/java/src/org/chromium/components/embedder_support/browser_context/BrowserContextHandle.java",
   "+components/webapk/android/libs/client",
diff --git a/components/installedapp/android/installed_app_verifier.cc b/components/installedapp/android/installed_app_verifier.cc
index e8a6789..9ab16f2 100644
--- a/components/installedapp/android/installed_app_verifier.cc
+++ b/components/installedapp/android/installed_app_verifier.cc
@@ -8,7 +8,7 @@
 #include "base/android/callback_android.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
-#include "components/digital_asset_links/digital_asset_links_handler.h"
+#include "components/content_relationship_verification/digital_asset_links_handler.h"
 #include "components/installedapp/android/jni_headers/InstalledAppProviderImpl_jni.h"
 #include "content/public/browser/android/browser_context_handle.h"
 #include "content/public/browser/browser_context.h"
@@ -19,11 +19,13 @@
 namespace {
 
 void DidGetResult(
-    std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler> handler,
+    std::unique_ptr<content_relationship_verification::DigitalAssetLinksHandler>
+        handler,
     base::OnceCallback<void(bool)> callback,
-    digital_asset_links::RelationshipCheckResult result) {
+    content_relationship_verification::RelationshipCheckResult result) {
   std::move(callback).Run(
-      result == digital_asset_links::RelationshipCheckResult::kSuccess);
+      result ==
+      content_relationship_verification::RelationshipCheckResult::kSuccess);
 }
 
 }  // namespace
@@ -45,10 +47,10 @@
       base::BindOnce(&base::android::RunBooleanCallbackAndroid,
                      base::android::ScopedJavaGlobalRef<jobject>(jcallback));
 
-  auto handler =
-      std::make_unique<digital_asset_links::DigitalAssetLinksHandler>(
-          browser_context->GetDefaultStoragePartition()
-              ->GetURLLoaderFactoryForBrowserProcess());
+  auto handler = std::make_unique<
+      content_relationship_verification::DigitalAssetLinksHandler>(
+      browser_context->GetDefaultStoragePartition()
+          ->GetURLLoaderFactoryForBrowserProcess());
   auto* handler_ptr = handler.get();
 
   // |handler| is owned by the callback, so it will be valid until the execution
diff --git a/components/metrics/data_use_tracker.cc b/components/metrics/data_use_tracker.cc
index 362a3d575..68f5e7e2 100644
--- a/components/metrics/data_use_tracker.cc
+++ b/components/metrics/data_use_tracker.cc
@@ -123,14 +123,15 @@
   const base::Time current_date = GetCurrentMeasurementDate();
   const base::Time week_ago = current_date - base::Days(7);
 
-  base::Value user_pref_new_dict{base::Value::Type::DICT};
+  base::Value::Dict user_pref_new_dict;
   for (const auto it : user_pref_dict) {
     base::Time key_date;
     if (base::Time::FromUTCString(it.first.c_str(), &key_date) &&
-        key_date > week_ago)
-      user_pref_new_dict.SetPath(it.first, it.second.Clone());
+        key_date > week_ago) {
+      user_pref_new_dict.Set(it.first, it.second.Clone());
+    }
   }
-  local_state_->Set(pref_name, user_pref_new_dict);
+  local_state_->SetDict(pref_name, std::move(user_pref_new_dict));
 }
 
 // Note: We compute total data use regardless of what is the current date. In
diff --git a/components/metrics/generate_expired_histograms_array.gni b/components/metrics/generate_expired_histograms_array.gni
index d3c34904e..a7e2b36 100644
--- a/components/metrics/generate_expired_histograms_array.gni
+++ b/components/metrics/generate_expired_histograms_array.gni
@@ -63,6 +63,7 @@
       "//tools/metrics/histograms/metadata/cookie/histograms.xml",
       "//tools/metrics/histograms/metadata/cras/histograms.xml",
       "//tools/metrics/histograms/metadata/cros/histograms.xml",
+      "//tools/metrics/histograms/metadata/cros_audio/histograms.xml",
       "//tools/metrics/histograms/metadata/cros_ml/histograms.xml",
       "//tools/metrics/histograms/metadata/cross_device/histograms.xml",
       "//tools/metrics/histograms/metadata/crostini/histograms.xml",
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 4a6febc..091113eb 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -715,6 +715,7 @@
   if (build_with_tflite_lib) {
     sources += [
       "autocomplete_scoring_model_executor_unittest.cc",
+      "autocomplete_scoring_model_handler_unittest.cc",
       "on_device_tail_model_executor_unittest.cc",
     ]
   }
@@ -733,6 +734,8 @@
     "//components/ntp_tiles:ntp_tiles",
     "//components/omnibox/common",
     "//components/open_from_clipboard:test_support",
+    "//components/optimization_guide/core:test_support",
+    "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/prefs:test_support",
     "//components/search",
     "//components/search_engines",
diff --git a/components/omnibox/browser/autocomplete_scoring_model_handler.cc b/components/omnibox/browser/autocomplete_scoring_model_handler.cc
index 7feb8bc..8ded5cd 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_handler.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_handler.cc
@@ -4,29 +4,66 @@
 
 #include "components/omnibox/browser/autocomplete_scoring_model_handler.h"
 
+#include <cmath>
 #include <memory>
 
+#include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/omnibox/browser/autocomplete_scoring_model_executor.h"
 #include "components/optimization_guide/core/model_handler.h"
 #include "components/optimization_guide/core/optimization_guide_model_provider.h"
+#include "components/optimization_guide/proto/autocomplete_scoring_model_metadata.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 using ModelInput = AutocompleteScoringModelExecutor::ModelInput;
 using ModelOutput = AutocompleteScoringModelExecutor::ModelOutput;
+using ScoringSignals = ::metrics::OmniboxEventProto::Suggestion::ScoringSignals;
+using ::optimization_guide::proto::AutocompleteScoringModelMetadata;
 using ::optimization_guide::proto::OptimizationTarget;
+using ::optimization_guide::proto::ScoringSignalSpec;
+using ::optimization_guide::proto::ScoringSignalTransformation;
+
+constexpr float kDefaultMissingValue = -1;
+
+namespace {
+
+// Checks if the signal value is valid.
+bool IsValidSignal(float val, const ScoringSignalSpec& signal_spec) {
+  if (signal_spec.has_min_value() && val < signal_spec.min_value()) {
+    return false;
+  }
+  if (signal_spec.has_max_value() && val > signal_spec.max_value()) {
+    return false;
+  }
+  return true;
+}
+
+// Transforms the input signals according to the configured transformation.
+float TransformSignal(float val, ScoringSignalTransformation transformation) {
+  switch (transformation) {
+    case optimization_guide::proto::SCORING_SIGNAL_TRANSFORMATION_LOG_2:
+      return log2(val + 1);
+    case optimization_guide::proto::SCORING_SIGNAL_TRANSFORMATION_UNKNOWN:
+    default:
+      DVLOG(0) << "Unknown scoring signal transformation type!";
+      return val;
+  }
+}
+
+}  // namespace
 
 AutocompleteScoringModelHandler::AutocompleteScoringModelHandler(
     optimization_guide::OptimizationGuideModelProvider* model_provider,
     scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner,
+    std::unique_ptr<AutocompleteScoringModelExecutor> model_executor,
     OptimizationTarget optimization_target,
     const absl::optional<optimization_guide::proto::Any>& model_metadata)
     : optimization_guide::ModelHandler<ModelOutput, ModelInput>(
           model_provider,
           model_executor_task_runner,
-          std::make_unique<AutocompleteScoringModelExecutor>(),
+          std::move(model_executor),
           /*model_inference_timeout=*/absl::nullopt,
           optimization_target,
           model_metadata) {
@@ -35,3 +72,201 @@
 }
 
 AutocompleteScoringModelHandler::~AutocompleteScoringModelHandler() = default;
+
+absl::optional<std::vector<float>>
+AutocompleteScoringModelHandler::GetModelInput(
+    const metrics::OmniboxEventProto::Suggestion::ScoringSignals&
+        scoring_signals) {
+  if (!ModelAvailable()) {
+    return absl::nullopt;
+  }
+
+  absl::optional<AutocompleteScoringModelMetadata> model_metadata =
+      ParsedSupportedFeaturesForLoadedModel<AutocompleteScoringModelMetadata>();
+  if (!model_metadata) {
+    return absl::nullopt;
+  }
+
+  return ExtractInputFromScoringSignals(scoring_signals,
+                                        model_metadata.value());
+}
+
+std::vector<float>
+AutocompleteScoringModelHandler::ExtractInputFromScoringSignals(
+    const ScoringSignals& scoring_signals,
+    const AutocompleteScoringModelMetadata& metadata) {
+  std::vector<float> model_input;
+  for (const auto& scoring_signal_spec : metadata.scoring_signal_specs()) {
+    absl::optional<float> val;
+    switch (scoring_signal_spec.type()) {
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_TYPED_COUNT:
+        if (scoring_signals.has_typed_count()) {
+          val = static_cast<float>(scoring_signals.typed_count());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_VISIT_COUNT:
+        if (scoring_signals.has_visit_count()) {
+          val = static_cast<float>(scoring_signals.visit_count());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_ELAPSED_TIME_LAST_VISIT_SECS:
+        if (scoring_signals.has_elapsed_time_last_visit_secs()) {
+          val = static_cast<float>(
+              scoring_signals.elapsed_time_last_visit_secs());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_IS_HOST_ONLY:
+        if (scoring_signals.has_is_host_only()) {
+          val = static_cast<float>(scoring_signals.is_host_only());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_LENGTH_OF_URL:
+        if (scoring_signals.has_length_of_url()) {
+          val = static_cast<float>(scoring_signals.length_of_url());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_FIRST_URL_MATCH_POSITION:
+        if (scoring_signals.has_first_url_match_position()) {
+          val = static_cast<float>(scoring_signals.first_url_match_position());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_HOST_MATCH_AT_WORD_BOUNDARY:
+        if (scoring_signals.has_host_match_at_word_boundary()) {
+          val =
+              static_cast<float>(scoring_signals.host_match_at_word_boundary());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_TOTAL_URL_MATCH_LENGTH:
+        if (scoring_signals.has_total_url_match_length()) {
+          val = static_cast<float>(scoring_signals.total_url_match_length());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_TOTAL_HOST_MATCH_LENGTH:
+        if (scoring_signals.has_total_host_match_length()) {
+          val = static_cast<float>(scoring_signals.total_host_match_length());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_TOTAL_PATH_MATCH_LENGTH:
+        if (scoring_signals.has_total_path_match_length()) {
+          val = static_cast<float>(scoring_signals.total_path_match_length());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_TOTAL_QUERY_OR_REF_MATCH_LENGTH:
+        if (scoring_signals.has_total_query_or_ref_match_length()) {
+          val = static_cast<float>(
+              scoring_signals.total_query_or_ref_match_length());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_TOTAL_TITLE_MATCH_LENGTH:
+        if (scoring_signals.has_total_title_match_length()) {
+          val = static_cast<float>(scoring_signals.total_title_match_length());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_NUM_INPUT_TERMS_MATCHED_BY_TITLE:
+        if (scoring_signals.has_num_input_terms_matched_by_title()) {
+          val = static_cast<float>(
+              scoring_signals.num_input_terms_matched_by_title());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_NUM_INPUT_TERMS_MATCHED_BY_URL:
+        if (scoring_signals.has_num_input_terms_matched_by_url()) {
+          val = static_cast<float>(
+              scoring_signals.num_input_terms_matched_by_url());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_ALLOWED_TO_BE_DEFAULT_MATCH:
+        if (scoring_signals.has_allowed_to_be_default_match()) {
+          val =
+              static_cast<float>(scoring_signals.allowed_to_be_default_match());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_HAS_NON_SCHEME_WWW_MATCH:
+        if (scoring_signals.has_has_non_scheme_www_match()) {
+          val = static_cast<float>(scoring_signals.has_non_scheme_www_match());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_TOTAL_BOOKMARK_TITLE_MATCH_LENGTH:
+        if (scoring_signals.has_total_bookmark_title_match_length()) {
+          val = static_cast<float>(
+              scoring_signals.total_bookmark_title_match_length());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_FIRST_BOOKMARK_TITLE_MATCH_POSITION:
+        if (scoring_signals.has_first_bookmark_title_match_position()) {
+          val = static_cast<float>(
+              scoring_signals.first_bookmark_title_match_position());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_NUM_BOOKMARKS_OF_URL:
+        if (scoring_signals.has_num_bookmarks_of_url()) {
+          val = static_cast<float>(scoring_signals.num_bookmarks_of_url());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_SHORTCUT_VISIT_COUNT:
+        if (scoring_signals.has_shortcut_visit_count()) {
+          val = static_cast<float>(scoring_signals.shortcut_visit_count());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_SHORTEST_SHORTCUT_LEN:
+        if (scoring_signals.has_shortest_shortcut_len()) {
+          val = static_cast<float>(scoring_signals.shortest_shortcut_len());
+        }
+        break;
+      case optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_ELAPSED_TIME_LAST_SHORTCUT_VISIT_SEC:
+        if (scoring_signals.has_elapsed_time_last_shortcut_visit_sec()) {
+          val = static_cast<float>(
+              scoring_signals.elapsed_time_last_shortcut_visit_sec());
+        }
+        break;
+      case optimization_guide::proto::SCORING_SIGNAL_TYPE_UNKNOWN:
+      default:
+        // Reached when the metadata is updated to have a new signal that
+        // the binary hasn't yet been updated to have; or when the binary
+        // has updated to remove a previous signal that the metadata hasn't
+        // yet been updated to remove.
+        DVLOG(0) << "Unknown scoring signal enum type in model metadata!";
+        break;
+    }
+
+    // Treat invalid signals as missing. Invalid signals may be caused by
+    // client errors, e.g., negative elapsed time.
+    if (val && !IsValidSignal(*val, scoring_signal_spec)) {
+      DVLOG(0) << "Invalid scoring signal value of '"
+               << optimization_guide::proto::ScoringSignalType_Name(
+                      scoring_signal_spec.type())
+               << "': " << *val;
+      val = absl::nullopt;
+    }
+
+    if (val && scoring_signal_spec.has_transformation()) {
+      val = TransformSignal(*val, scoring_signal_spec.transformation());
+    }
+
+    // Set default value if missing.
+    if (!val) {
+      val = scoring_signal_spec.has_missing_value()
+                ? scoring_signal_spec.missing_value()
+                : kDefaultMissingValue;
+    }
+
+    model_input.push_back(*val);
+  }
+  DCHECK_EQ(static_cast<size_t>(model_input.size()),
+            static_cast<size_t>(metadata.scoring_signal_specs().size()));
+  return model_input;
+}
diff --git a/components/omnibox/browser/autocomplete_scoring_model_handler.h b/components/omnibox/browser/autocomplete_scoring_model_handler.h
index 90b7295..220ae33 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_handler.h
+++ b/components/omnibox/browser/autocomplete_scoring_model_handler.h
@@ -5,12 +5,15 @@
 #ifndef COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_SCORING_MODEL_HANDLER_H_
 #define COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_SCORING_MODEL_HANDLER_H_
 
+#include "base/gtest_prod_util.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/omnibox/browser/autocomplete_scoring_model_executor.h"
 #include "components/optimization_guide/core/model_handler.h"
 #include "components/optimization_guide/core/optimization_guide_model_provider.h"
+#include "components/optimization_guide/proto/autocomplete_scoring_model_metadata.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
 
 // Implements optimization_guide::ModelHandler for autocomplete scoring.
 // Keeps scoring model in memory.
@@ -22,6 +25,7 @@
   AutocompleteScoringModelHandler(
       optimization_guide::OptimizationGuideModelProvider* model_provider,
       scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner,
+      std::unique_ptr<AutocompleteScoringModelExecutor> model_executor,
       optimization_guide::proto::OptimizationTarget optimization_target,
       const absl::optional<optimization_guide::proto::Any>& model_metadata);
   ~AutocompleteScoringModelHandler() override;
@@ -31,6 +35,26 @@
       delete;
   AutocompleteScoringModelHandler& operator=(
       const AutocompleteScoringModelHandler&) = delete;
+
+  // Construct the model input from scoring signals. Signals are appended to the
+  // input vector in the same order as signal specifications in the metadata.
+  // Checks validness of signals and applies transformation if configured in
+  // metadata. Returns nullopt if the model or metadata is missing.
+  absl::optional<std::vector<float>> GetModelInput(
+      const metrics::OmniboxEventProto::Suggestion::ScoringSignals&
+          scoring_signals);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(AutocompleteScoringModelHandlerTest,
+                           ExtractInputFromScoringSignalsTest);
+
+  // Extracts the model input from scoring signals according to the model
+  // metadata.
+  std::vector<float> ExtractInputFromScoringSignals(
+      const metrics::OmniboxEventProto::Suggestion::ScoringSignals&
+          scoring_signals,
+      const optimization_guide::proto::AutocompleteScoringModelMetadata&
+          metadata);
 };
 
 #endif  // COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_SCORING_MODEL_HANDLER_H_
diff --git a/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc b/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc
new file mode 100644
index 0000000..c636878
--- /dev/null
+++ b/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc
@@ -0,0 +1,128 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/omnibox/browser/autocomplete_scoring_model_handler.h"
+
+#include "base/task/sequenced_task_runner.h"
+#include "base/test/task_environment.h"
+#include "components/omnibox/browser/autocomplete_scoring_model_executor.h"
+#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
+#include "components/optimization_guide/proto/autocomplete_scoring_model_metadata.pb.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
+
+using ScoringSignals = ::metrics::OmniboxEventProto::Suggestion::ScoringSignals;
+using ::optimization_guide::proto::AutocompleteScoringModelMetadata;
+using ::optimization_guide::proto::ScoringSignalSpec;
+using ::optimization_guide::proto::ScoringSignalTransformation;
+using ::optimization_guide::proto::ScoringSignalType;
+
+namespace {
+
+ScoringSignalSpec CreateScoringSignalSpec(
+    ScoringSignalType type,
+    absl::optional<ScoringSignalTransformation> transformation = absl::nullopt,
+    absl::optional<float> min_val = absl::nullopt,
+    absl::optional<float> max_val = absl::nullopt,
+    absl::optional<float> missing_val = absl::nullopt) {
+  ScoringSignalSpec spec;
+  spec.set_type(type);
+  if (transformation) {
+    spec.set_transformation(*transformation);
+  }
+  if (min_val) {
+    spec.set_min_value(*min_val);
+  }
+  if (max_val) {
+    spec.set_max_value(*max_val);
+  }
+  if (missing_val) {
+    spec.set_missing_value(*missing_val);
+  }
+  return spec;
+}
+
+}  // namespace
+
+class TestAutocompleteScoringModelExecutor
+    : public AutocompleteScoringModelExecutor {
+ public:
+  TestAutocompleteScoringModelExecutor() = default;
+  ~TestAutocompleteScoringModelExecutor() override = default;
+
+  void InitializeAndMoveToExecutionThread(
+      absl::optional<base::TimeDelta>,
+      optimization_guide::proto::OptimizationTarget,
+      scoped_refptr<base::SequencedTaskRunner>,
+      scoped_refptr<base::SequencedTaskRunner>) override {}
+
+  void UpdateModelFile(const base::FilePath&) override {}
+
+  void UnloadModel() override {}
+
+  void SetShouldUnloadModelOnComplete(bool should_auto_unload) override {}
+};
+
+class AutocompleteScoringModelHandlerTest : public testing::Test {
+ public:
+  AutocompleteScoringModelHandlerTest() = default;
+  ~AutocompleteScoringModelHandlerTest() override = default;
+
+  void SetUp() override {
+    model_provider_ = std::make_unique<
+        optimization_guide::TestOptimizationGuideModelProvider>();
+    model_handler_ = std::make_unique<AutocompleteScoringModelHandler>(
+        model_provider_.get(), task_environment_.GetMainThreadTaskRunner(),
+        std::make_unique<TestAutocompleteScoringModelExecutor>(),
+        /*optimization_target=*/
+        optimization_guide::proto::OPTIMIZATION_TARGET_OMNIBOX_URL_SCORING,
+        /*model_metadata=*/absl::nullopt);
+  }
+
+  void TearDown() override {
+    model_handler_.reset();
+    model_provider_.reset();
+    RunUntilIdle();
+  }
+
+  void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
+      model_provider_;
+  std::unique_ptr<AutocompleteScoringModelHandler> model_handler_;
+};
+
+TEST_F(AutocompleteScoringModelHandlerTest,
+       ExtractInputFromScoringSignalsTest) {
+  // Metadata with three scoring signal specifications.
+  AutocompleteScoringModelMetadata model_metadata;
+  *model_metadata.add_scoring_signal_specs() = CreateScoringSignalSpec(
+      optimization_guide::proto::SCORING_SIGNAL_TYPE_LENGTH_OF_URL);
+  // Signal with log2 transformation.
+  *model_metadata.add_scoring_signal_specs() = CreateScoringSignalSpec(
+      optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_ELAPSED_TIME_LAST_VISIT_SECS,
+      /*transformation=*/optimization_guide::proto::
+          SCORING_SIGNAL_TRANSFORMATION_LOG_2);
+  // Invalid signal.
+  *model_metadata.add_scoring_signal_specs() = CreateScoringSignalSpec(
+      optimization_guide::proto::
+          SCORING_SIGNAL_TYPE_ELAPSED_TIME_LAST_SHORTCUT_VISIT_SEC,
+      /*transformation=*/absl::nullopt,
+      /*min_val=*/0, /*max_val=*/absl::nullopt, /*missing_val=*/-2);
+
+  // Scoring signals.
+  ScoringSignals scoring_signals;
+  scoring_signals.set_length_of_url(10);
+  scoring_signals.set_elapsed_time_last_visit_secs(511);
+  scoring_signals.set_elapsed_time_last_shortcut_visit_sec(-200);
+
+  const auto input_signals = model_handler_->ExtractInputFromScoringSignals(
+      scoring_signals, model_metadata);
+  EXPECT_THAT(input_signals, testing::UnorderedElementsAre(10, 9, -2));
+}
\ No newline at end of file
diff --git a/components/omnibox/browser/autocomplete_scoring_model_service.cc b/components/omnibox/browser/autocomplete_scoring_model_service.cc
index f330350f..b88695d 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_service.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_service.cc
@@ -26,6 +26,7 @@
     url_scoring_model_handler_ =
         std::make_unique<AutocompleteScoringModelHandler>(
             model_provider, model_executor_task_runner_.get(),
+            std::make_unique<AutocompleteScoringModelExecutor>(),
             optimization_guide::proto::OPTIMIZATION_TARGET_OMNIBOX_URL_SCORING,
             /*model_metadata=*/absl::nullopt);
   }
@@ -34,12 +35,21 @@
 AutocompleteScoringModelService::~AutocompleteScoringModelService() = default;
 
 void AutocompleteScoringModelService::ScoreAutocompleteUrlMatch(
-    AutocompleteScoringModelExecutor::ModelInput input_signals,
+    const metrics::OmniboxEventProto::Suggestion::ScoringSignals&
+        scoring_signals,
     base::OnceCallback<void(
         const absl::optional<AutocompleteScoringModelExecutor::ModelOutput>&)>
         scoring_callback) {
-  if (url_scoring_model_handler_ != nullptr) {
-    url_scoring_model_handler_->ExecuteModelWithInput(
-        std::move(scoring_callback), input_signals);
+  if (!url_scoring_model_handler_) {
+    return;
   }
+
+  absl::optional<std::vector<float>> input_signals =
+      url_scoring_model_handler_->GetModelInput(scoring_signals);
+  if (!input_signals) {
+    return;
+  }
+
+  url_scoring_model_handler_->ExecuteModelWithInput(std::move(scoring_callback),
+                                                    *input_signals);
 }
diff --git a/components/omnibox/browser/autocomplete_scoring_model_service.h b/components/omnibox/browser/autocomplete_scoring_model_service.h
index 0021b7f..10c2445 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_service.h
+++ b/components/omnibox/browser/autocomplete_scoring_model_service.h
@@ -15,6 +15,7 @@
 #include "components/omnibox/browser/autocomplete_scoring_model_handler.h"
 #include "components/optimization_guide/core/optimization_guide_model_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
 
 // Autocomplete scoring service using machine learning models via
 // OptimizationGuide's model handler.
@@ -32,7 +33,8 @@
 
   // Scores an autocomplete URL match with scoring signals.
   void ScoreAutocompleteUrlMatch(
-      AutocompleteScoringModelExecutor::ModelInput input_signals,
+      const metrics::OmniboxEventProto::Suggestion::ScoringSignals&
+          scoring_signals,
       base::OnceCallback<void(
           const absl::optional<AutocompleteScoringModelExecutor::ModelOutput>&)>
           scoring_callback);
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index f5acbb59c..38f1f6d 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -1147,6 +1147,65 @@
     client_->OnBookmarkLaunched();
 }
 
+void OmniboxEditModel::OpenSelection(OmniboxPopupSelection selection,
+                                     base::TimeTicks timestamp,
+                                     WindowOpenDisposition disposition) {
+  // Intentionally accept input when selection has no line.
+  // This will reach `OpenMatch` indirectly.
+  if (selection.line >= result().size()) {
+    AcceptInput(disposition, timestamp);
+    return;
+  }
+
+  // Keyword mode is handled separately, and other selections should
+  // only be opened when there is a popup view.
+  DCHECK_NE(selection.state, OmniboxPopupSelection::KEYWORD_MODE);
+  DCHECK(popup_view_);
+
+  const AutocompleteMatch& match = result().match_at(selection.line);
+
+  // TODO(crbug/1408506): This is an exceptional quirk for the new
+  //  match takeover actions mechanism, and can likely be simplified.
+  const OmniboxAction* action = match.GetPrimaryAction();
+  if (selection.state == OmniboxPopupSelection::NORMAL &&
+      (action == nullptr || !action->TakesOverMatch())) {
+    AcceptInput(disposition, timestamp);
+    return;
+  }
+
+  if (selection.state == OmniboxPopupSelection::FOCUSED_BUTTON_HEADER) {
+    DCHECK(match.suggestion_group_id.has_value());
+
+    const bool current_visibility = result().IsSuggestionGroupHidden(
+        GetPrefService(), match.suggestion_group_id.value());
+    result().SetSuggestionGroupHidden(GetPrefService(),
+                                      match.suggestion_group_id.value(),
+                                      !current_visibility);
+  } else if (selection.state ==
+             OmniboxPopupSelection::FOCUSED_BUTTON_REMOVE_SUGGESTION) {
+    TryDeletingPopupLine(selection.line);
+  } else if (selection.state == OmniboxPopupSelection::FOCUSED_BUTTON_ACTION) {
+    // TODO(crbug/1408506): Either handle in OpenMatch or convert OpenMatch
+    //  usage to OpenSelection so this can keep a consistent code
+    //  and metrics flow. The broad goal is to eliminate one or the other,
+    //  or at least hide one as private so call sites are all consistent.
+    DCHECK(timestamp != base::TimeTicks());
+    ExecuteAction(selection, disposition, timestamp);
+  } else {
+    DCHECK(timestamp != base::TimeTicks());
+    if (selection.state == OmniboxPopupSelection::FOCUSED_BUTTON_TAB_SWITCH) {
+      disposition = WindowOpenDisposition::SWITCH_TO_TAB;
+    }
+    OpenMatch(match, disposition, GURL(), std::u16string(),
+              GetPopupSelection().line, timestamp);
+  }
+}
+
+void OmniboxEditModel::OpenSelection(base::TimeTicks timestamp,
+                                     WindowOpenDisposition disposition) {
+  OpenSelection(GetPopupSelection(), timestamp, disposition);
+}
+
 bool OmniboxEditModel::InExplicitExperimentalKeywordMode() {
   return AutocompleteProvider::InExplicitExperimentalKeywordMode(input_,
                                                                  keyword_);
@@ -2126,56 +2185,6 @@
   return selection.IsControlPresentOnMatch(result(), GetPrefService());
 }
 
-bool OmniboxEditModel::TriggerPopupSelectionAction(
-    OmniboxPopupSelection selection,
-    base::TimeTicks timestamp,
-    WindowOpenDisposition disposition) {
-  DCHECK(popup_view_);
-
-  // Early exit for the kNoMatch case. Also exits if the calling UI passes in
-  // an invalid |selection|.
-  if (selection.line >= result().size())
-    return false;
-
-  auto& match = result().match_at(selection.line);
-  switch (selection.state) {
-    case OmniboxPopupSelection::FOCUSED_BUTTON_HEADER: {
-      DCHECK(match.suggestion_group_id.has_value());
-
-      const bool current_visibility = result().IsSuggestionGroupHidden(
-          GetPrefService(), match.suggestion_group_id.value());
-      result().SetSuggestionGroupHidden(GetPrefService(),
-                                        match.suggestion_group_id.value(),
-                                        !current_visibility);
-      break;
-    }
-    case OmniboxPopupSelection::NORMAL: {
-      return ExecuteTakeoverAction(selection.line, disposition, timestamp);
-    }
-    case OmniboxPopupSelection::FOCUSED_BUTTON_TAB_SWITCH: {
-      DCHECK(timestamp != base::TimeTicks());
-      OpenMatch(match, WindowOpenDisposition::SWITCH_TO_TAB, GURL(),
-                std::u16string(), GetPopupSelection().line, timestamp);
-      break;
-    }
-    case OmniboxPopupSelection::FOCUSED_BUTTON_ACTION: {
-      DCHECK(timestamp != base::TimeTicks());
-      ExecuteAction(selection, disposition, timestamp);
-      break;
-    }
-    case OmniboxPopupSelection::FOCUSED_BUTTON_REMOVE_SUGGESTION: {
-      TryDeletingPopupLine(selection.line);
-      break;
-    }
-    default: {
-      // Behavior is not yet supported.
-      return false;
-    }
-  }
-
-  return true;
-}
-
 void OmniboxEditModel::TryDeletingPopupLine(size_t line) {
   DCHECK(popup_view_);
 
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index c9a49e2..2ff3af9 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -251,6 +251,19 @@
                  size_t index,
                  base::TimeTicks match_selection_timestamp = base::TimeTicks());
 
+  // Opens given selection. Most kinds of selection invoke an action or
+  // otherwise call `OpenMatch`, but some may `AcceptInput`.
+  void OpenSelection(
+      OmniboxPopupSelection selection,
+      base::TimeTicks timestamp = base::TimeTicks(),
+      WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB);
+
+  // A simplified version of OpenSelection that opens the model's current
+  // selection.
+  void OpenSelection(
+      base::TimeTicks timestamp = base::TimeTicks(),
+      WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB);
+
   OmniboxFocusState focus_state() const { return focus_state_; }
   bool has_focus() const { return focus_state_ != OMNIBOX_FOCUS_NONE; }
 
@@ -464,18 +477,6 @@
   // should query to decide whether or not to draw the control.
   bool IsPopupControlPresentOnMatch(OmniboxPopupSelection selection) const;
 
-  // On popup, triggers the action on |selection| (usually an auxiliary button).
-  // If the popup model supports the action and performs it, this returns true.
-  // This can't handle all actions currently, and returns false in those cases.
-  // The timestamp parameter is currently only used by FOCUSED_BUTTON_TAB_SWITCH
-  // and FOCUSED_BUTTON_ACTION, so is set by default for other use cases.
-  // The `disposition` can be used to respect keyboard state for opening
-  // actions in background tabs, new windows, etc.
-  bool TriggerPopupSelectionAction(
-      OmniboxPopupSelection selection,
-      base::TimeTicks timestamp = base::TimeTicks(),
-      WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB);
-
   // From popup, tries to erase the suggestion at |line|. This should determine
   // if the item at |line| can be removed from history, and if so, remove it
   // and update the popup.
diff --git a/components/optimization_guide/proto/autocomplete_scoring_model_metadata.proto b/components/optimization_guide/proto/autocomplete_scoring_model_metadata.proto
index 388f0d93..b5349561 100644
--- a/components/optimization_guide/proto/autocomplete_scoring_model_metadata.proto
+++ b/components/optimization_guide/proto/autocomplete_scoring_model_metadata.proto
@@ -25,8 +25,8 @@
   SCORING_SIGNAL_TYPE_TOTAL_PATH_MATCH_LENGTH = 10;
   SCORING_SIGNAL_TYPE_TOTAL_QUERY_OR_REF_MATCH_LENGTH = 11;
   SCORING_SIGNAL_TYPE_TOTAL_TITLE_MATCH_LENGTH = 12;
-  SCORING_SIGNAL_TYPE_TITLE_MATCH_RATIO = 13;
-  SCORING_SIGNAL_TYPE_URL_MATCH_RATIO = 14;
+  SCORING_SIGNAL_TYPE_NUM_INPUT_TERMS_MATCHED_BY_TITLE = 13;
+  SCORING_SIGNAL_TYPE_NUM_INPUT_TERMS_MATCHED_BY_URL = 14;
   SCORING_SIGNAL_TYPE_ALLOWED_TO_BE_DEFAULT_MATCH = 15;
   SCORING_SIGNAL_TYPE_HAS_NON_SCHEME_WWW_MATCH = 16;
   SCORING_SIGNAL_TYPE_TOTAL_BOOKMARK_TITLE_MATCH_LENGTH = 17;
@@ -41,7 +41,7 @@
 enum ScoringSignalTransformation {
   // No transformation.
   SCORING_SIGNAL_TRANSFORMATION_UNKNOWN = 0;
-  // log2(x).
+  // log2(1+x).
   SCORING_SIGNAL_TRANSFORMATION_LOG_2 = 1;
 }
 
@@ -53,6 +53,10 @@
   optional float missing_value = 2;
   // Transformation to apply to this signal.
   optional ScoringSignalTransformation transformation = 3;
+  // Minimum value of valid signals.
+  optional float min_value = 4;
+  // Maximum value of valid signals.
+  optional float max_value = 5;
 }
 
 // The message contains a set of params to run a specific autocomplete scoring
diff --git a/components/paint_preview/browser/file_manager_unittest.cc b/components/paint_preview/browser/file_manager_unittest.cc
index 4303f183..b591de7e 100644
--- a/components/paint_preview/browser/file_manager_unittest.cc
+++ b/components/paint_preview/browser/file_manager_unittest.cc
@@ -69,8 +69,7 @@
 
   base::FilePath file_path = out.AppendASCII("test");
   std::string test_str = "Hello World!";
-  EXPECT_EQ(static_cast<int>(test_str.length()),
-            base::WriteFile(file_path, test_str.data(), test_str.length()));
+  EXPECT_TRUE(base::WriteFile(file_path, test_str));
 
   EXPECT_EQ(manager->GetSizeOfArtifacts(valid_key), test_str.length());
 }
@@ -92,9 +91,7 @@
             EXPECT_FALSE(directory.empty());
             base::FilePath test_file = directory.AppendASCII("test");
             std::string test_str = "Hello World!";
-            EXPECT_EQ(
-                static_cast<int>(test_str.length()),
-                base::WriteFile(test_file, test_str.data(), test_str.length()));
+            EXPECT_TRUE(base::WriteFile(test_file, test_str));
 
             // Open an existing directory and don't clear.
             base::FilePath existing_directory =
@@ -126,8 +123,7 @@
   // A file needs to exist for compression to work.
   base::FilePath test_file = directory.AppendASCII("test");
   std::string test_str = "Hello World!";
-  EXPECT_EQ(static_cast<int>(test_str.length()),
-            base::WriteFile(test_file, test_str.data(), test_str.length()));
+  EXPECT_TRUE(base::WriteFile(test_file, test_str));
   EXPECT_TRUE(base::PathExists(test_file));
   base::FilePath test_file_empty = directory.AppendASCII("foo.txt");
   {
diff --git a/components/paint_preview/common/file_utils.cc b/components/paint_preview/common/file_utils.cc
index 719222c9..424807e 100644
--- a/components/paint_preview/common/file_utils.cc
+++ b/components/paint_preview/common/file_utils.cc
@@ -18,7 +18,7 @@
   std::string proto_str;
   if (!proto.SerializeToString(&proto_str))
     return false;
-  return base::WriteFile(file_path, proto_str.data(), proto_str.size()) > 0;
+  return base::WriteFile(file_path, proto_str);
 }
 
 std::unique_ptr<PaintPreviewProto> ReadProtoFromFile(
diff --git a/components/paint_preview/player/player_compositor_delegate_unittest.cc b/components/paint_preview/player/player_compositor_delegate_unittest.cc
index 580d871..4a1110d 100644
--- a/components/paint_preview/player/player_compositor_delegate_unittest.cc
+++ b/components/paint_preview/player/player_compositor_delegate_unittest.cc
@@ -297,7 +297,7 @@
               auto root_file = directory->AppendASCII("0.skp");
               proto->mutable_root_frame()->set_file_path(
                   root_file.AsUTF8Unsafe());
-              base::WriteFile(root_file, fake_data.data(), fake_data.size());
+              base::WriteFile(root_file, fake_data);
 
               if (!skip_proto_serialization) {
                 file_manager->SerializePaintPreviewProto(key, *proto, false);
@@ -366,12 +366,12 @@
             auto root_file = directory->AppendASCII("0.skp");
             proto->mutable_root_frame()->set_file_path(
                 root_file.AsUTF8Unsafe());
-            base::WriteFile(root_file, fake_data.data(), fake_data.size());
+            base::WriteFile(root_file, fake_data);
 
             auto subframe_file = directory->AppendASCII("1.skp");
             proto->mutable_subframes(0)->set_file_path(
                 subframe_file.AsUTF8Unsafe());
-            base::WriteFile(subframe_file, fake_data.data(), fake_data.size());
+            base::WriteFile(subframe_file, fake_data);
 
             file_manager->SerializePaintPreviewProto(key, *proto, false);
             std::move(quit).Run();
@@ -420,7 +420,7 @@
             auto directory = file_manager->CreateOrGetDirectory(key, true);
             std::string fake_data = "Hello World!";
             auto proto_file = directory->AppendASCII("proto.pb");
-            base::WriteFile(proto_file, fake_data.data(), fake_data.size());
+            base::WriteFile(proto_file, fake_data);
           },
           loop.QuitClosure(), file_manager, key));
 
@@ -670,8 +670,7 @@
           base::Unretained(&dir)));
   env.RunUntilIdle();
   std::string data = "foo";
-  EXPECT_TRUE(
-      base::WriteFile(dir.AppendASCII("test_file"), data.data(), data.size()));
+  EXPECT_TRUE(base::WriteFile(dir.AppendASCII("test_file"), data));
   {
     PlayerCompositorDelegateImpl player_compositor_delegate;
     player_compositor_delegate.SetExpected(CompositorStatus::NO_CAPTURE, 0.0);
diff --git a/components/password_manager/core/browser/ui/passwords_grouper.cc b/components/password_manager/core/browser/ui/passwords_grouper.cc
index 61c13e0..fe0c78a 100644
--- a/components/password_manager/core/browser/ui/passwords_grouper.cc
+++ b/components/password_manager/core/browser/ui/passwords_grouper.cc
@@ -21,17 +21,19 @@
 
 namespace {
 
+// Returns signon_realm for regular forms and formatted url for federated forms.
 std::string GetSignonRealm(const PasswordForm& form) {
+  std::string result = form.signon_realm;
   if (form.IsFederatedCredential()) {
-    return base::UTF16ToUTF8(url_formatter::FormatUrlForSecurityDisplay(
+    result = base::UTF16ToUTF8(url_formatter::FormatUrlForSecurityDisplay(
         form.url, url_formatter::SchemeDisplay::SHOW));
   }
-  // Remove trailing '/' because facets don't have it.
-  if (base::EndsWith(form.signon_realm, "/")) {
-    return form.signon_realm.substr(0, form.signon_realm.size() - 1);
+  // Remove trailing '/' because FacetURIs don't have it.
+  if (base::EndsWith(result, "/")) {
+    return result.substr(0, result.size() - 1);
   }
 
-  return form.signon_realm;
+  return result;
 }
 
 // An implementation of the disjoint-set data structure
@@ -360,12 +362,12 @@
     DCHECK(map_facet_to_group_id.contains(signon_realm));
     GroupId group_id = map_facet_to_group_id[signon_realm];
 
+    // Store group id for sign-on realm.
+    map_signon_realm_to_group_id[SignonRealm(form.signon_realm)] = group_id;
+
+    // Store form for username/password key.
     UsernamePasswordKey key(CreateUsernamePasswordSortKey(form));
     map_group_id_to_forms[group_id][key].push_back(std::move(form));
-
-    // Store group id for sign-on realm. Append "/" because
-    // PasswordForm::signon_realms should always has trailing '/'.
-    map_signon_realm_to_group_id[SignonRealm(signon_realm + "/")] = group_id;
   }
 }
 
diff --git a/components/password_manager/core/browser/ui/passwords_grouper_unittest.cc b/components/password_manager/core/browser/ui/passwords_grouper_unittest.cc
index 7740241f..7372bca 100644
--- a/components/password_manager/core/browser/ui/passwords_grouper_unittest.cc
+++ b/components/password_manager/core/browser/ui/passwords_grouper_unittest.cc
@@ -55,10 +55,11 @@
   blocked_form.blocked_by_user = true;
 
   PasswordForm federated_form;
-  federated_form.signon_realm = "https://federated.com/";
-  federated_form.username_value = u"example@gmail.com";
+  federated_form.url = GURL("https://test.org/");
+  federated_form.signon_realm = "federation://test.com/accounts.federation.com";
+  federated_form.username_value = u"username2";
   federated_form.federation_origin =
-      url::Origin::Create(GURL(u"federatedOrigin.com"));
+      url::Origin::Create(GURL("https://accounts.federation.com"));
 
   EXPECT_CALL(affiliation_service(), GetAllGroups)
       .WillRepeatedly(
@@ -92,10 +93,11 @@
   blocked_form.blocked_by_user = true;
 
   PasswordForm federated_form;
-  federated_form.signon_realm = "https://federated.com/";
-  federated_form.username_value = u"example@gmail.com";
+  federated_form.url = GURL("https://test.org/");
+  federated_form.signon_realm = "federation://test.com/accounts.federation.com";
+  federated_form.username_value = u"username2";
   federated_form.federation_origin =
-      url::Origin::Create(GURL(u"federatedOrigin.com"));
+      url::Origin::Create(GURL("https://accounts.federation.com"));
 
   GroupedFacets group;
   group.facets = {
@@ -132,10 +134,11 @@
   blocked_form.blocked_by_user = true;
 
   PasswordForm federated_form;
-  federated_form.signon_realm = "https://federated.com/";
-  federated_form.username_value = u"example@gmail.com";
+  federated_form.url = GURL("https://test.org/");
+  federated_form.signon_realm = "federation://test.com/accounts.federation.com";
+  federated_form.username_value = u"username2";
   federated_form.federation_origin =
-      url::Origin::Create(GURL(u"federatedOrigin.com"));
+      url::Origin::Create(GURL("https://accounts.federation.com"));
 
   EXPECT_CALL(affiliation_service(), GetAllGroups)
       .WillRepeatedly(
@@ -286,4 +289,38 @@
       ElementsAre(AffiliatedGroup({credential}, {GetShownOrigin(credential)})));
 }
 
+TEST_F(PasswordsGrouperTest, FederatedAndroidAppGroupedWithRegularPasswords) {
+  PasswordForm form = CreateForm("https://test.app.com/");
+  PasswordForm federated_android_form;
+  federated_android_form.signon_realm =
+      "android://"
+      "5Z0D_o6B8BqileZyWhXmqO_wkO8uO0etCEXvMn5tUzEqkWUgfTSjMcTM7eMMTY_"
+      "FGJC9RlpRNt_8Qp5tgDocXw==@com.bambuna.podcastaddict/";
+  federated_android_form.username_value = u"test@gmail.com";
+  federated_android_form.url = GURL(federated_android_form.signon_realm);
+  federated_android_form.federation_origin =
+      url::Origin::Create(GURL(u"https://federatedOrigin.com"));
+
+  GroupedFacets group;
+  group.facets = {
+      Facet(FacetURI::FromPotentiallyInvalidSpec(
+          "android://"
+          "5Z0D_o6B8BqileZyWhXmqO_wkO8uO0etCEXvMn5tUzEqkWUgfTSjMcTM7eMMTY_"
+          "FGJC9RlpRNt_8Qp5tgDocXw==@com.bambuna.podcastaddict")),
+      Facet(FacetURI::FromPotentiallyInvalidSpec("https://test.app.com")),
+  };
+
+  EXPECT_CALL(affiliation_service(), GetAllGroups)
+      .WillRepeatedly(
+          base::test::RunOnceCallback<0>(std::vector<GroupedFacets>{group}));
+  grouper().GroupPasswords({form, federated_android_form}, base::DoNothing());
+
+  CredentialUIEntry credential({form}),
+      federated_credential({federated_android_form});
+  EXPECT_THAT(
+      grouper().GetAffiliatedGroupsWithGroupingInfo(),
+      ElementsAre(AffiliatedGroup({federated_credential, credential},
+                                  {GetShownOrigin(federated_credential)})));
+}
+
 }  // namespace password_manager
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index d4b9fa5..8ee2bca 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -558,12 +558,13 @@
       "android/policy_converter_unittest.cc",
       "android/policy_map_android_unittest.cc",
       "android/policy_service_android_unittest.cc",
-      "policy_logger_unittest.cc",
     ]
   } else {
     sources += [ "async_policy_provider_unittest.cc" ]
   }
-  if (!is_android && !is_ios) {
+  if (is_android || is_ios) {
+    sources += [ "policy_logger_unittest.cc" ]
+  } else {
     sources += [
       "cloud/cloud_external_data_store_unittest.cc",
       "cloud/component_cloud_policy_service_unittest.cc",
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index d02a5bf..54a5444 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -44,6 +44,12 @@
              "PolicyMergeMultiSource",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+#if BUILDFLAG(IS_IOS)
+BASE_FEATURE(kPolicyLogsPageIOS,
+             "PolicyLogsPageIOS",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif  // BUILDFLAG(IS_IOS)
+
 }  // namespace features
 
 }  // namespace policy
diff --git a/components/policy/core/common/features.h b/components/policy/core/common/features.h
index 02ee846..831fa66 100644
--- a/components/policy/core/common/features.h
+++ b/components/policy/core/common/features.h
@@ -47,6 +47,11 @@
 // Prevent policies set by a single source from being treated as merged.
 POLICY_EXPORT BASE_DECLARE_FEATURE(kPolicyMergeMultiSource);
 
+#if BUILDFLAG(IS_IOS)
+// Enable logging and chrome://policy/logs page on IOS.
+POLICY_EXPORT BASE_DECLARE_FEATURE(kPolicyLogsPageIOS);
+#endif  // BUILDFLAG(IS_IOS)
+
 }  // namespace features
 }  // namespace policy
 
diff --git a/components/policy/core/common/policy_logger.cc b/components/policy/core/common/policy_logger.cc
index 2fa5a56e..ab8a64b 100644
--- a/components/policy/core/common/policy_logger.cc
+++ b/components/policy/core/common/policy_logger.cc
@@ -188,6 +188,8 @@
 bool PolicyLogger::IsPolicyLoggingEnabled() const {
 #if BUILDFLAG(IS_ANDROID)
   return base::FeatureList::IsEnabled(policy::features::kPolicyLogsPageAndroid);
+#elif BUILDFLAG(IS_IOS)
+  return base::FeatureList::IsEnabled(policy::features::kPolicyLogsPageIOS);
 #else
   return false;
 #endif  // BUILDFLAG(IS_ANDROID)
diff --git a/components/policy/core/common/policy_logger.h b/components/policy/core/common/policy_logger.h
index 2c2f9950..cfface8b 100644
--- a/components/policy/core/common/policy_logger.h
+++ b/components/policy/core/common/policy_logger.h
@@ -20,7 +20,7 @@
 // messages logged with DLOG are still important to be seen on the
 // chrome://policy/logs page in release mode. The DLOG call in StreamLog() will
 // do the check as usual for command line logging.
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 #define LOG_POLICY(log_severity, log_source)                                  \
   LOG_POLICY_##log_severity(::policy::PolicyLogger::LogHelper::LogType::kLog, \
                             log_source)
diff --git a/components/policy/core/common/policy_logger_unittest.cc b/components/policy/core/common/policy_logger_unittest.cc
index 2fec90da..e2cf528 100644
--- a/components/policy/core/common/policy_logger_unittest.cc
+++ b/components/policy/core/common/policy_logger_unittest.cc
@@ -25,8 +25,13 @@
 
 TEST(PolicyLoggerTest, PolicyLoggingEnabled) {
   base::test::ScopedFeatureList scoped_feature_list_;
+#if BUILDFLAG(IS_ANDROID)
   scoped_feature_list_.InitWithFeatureState(
       policy::features::kPolicyLogsPageAndroid, true);
+#elif BUILDFLAG(IS_IOS)
+  scoped_feature_list_.InitWithFeatureState(
+      policy::features::kPolicyLogsPageIOS, true);
+#endif
 
   PolicyLogger* policy_logger = policy::PolicyLogger::GetInstance();
 
@@ -42,8 +47,13 @@
 
 TEST(PolicyLoggerTest, PolicyLoggingDisabled) {
   base::test::ScopedFeatureList scoped_feature_list_;
+#if BUILDFLAG(IS_ANDROID)
   scoped_feature_list_.InitWithFeatureState(
       policy::features::kPolicyLogsPageAndroid, false);
+#elif BUILDFLAG(IS_IOS)
+  scoped_feature_list_.InitWithFeatureState(
+      policy::features::kPolicyLogsPageIOS, false);
+#endif
 
   PolicyLogger* policy_logger = policy::PolicyLogger::GetInstance();
 
@@ -53,4 +63,4 @@
             logs_size_before_adding);
 }
 
-}  // namespace policy
\ No newline at end of file
+}  // namespace policy
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
index def7970..11450543 100644
--- a/components/policy/core/common/schema.cc
+++ b/components/policy/core/common/schema.cc
@@ -1065,9 +1065,9 @@
     return false;
   }
   schema_node->extra = static_cast<int>(restriction_nodes_.size());
-  restriction_nodes_.emplace_back();
-  restriction_nodes_.back().enumeration_restriction.offset_begin = offset_begin;
-  restriction_nodes_.back().enumeration_restriction.offset_end = offset_end;
+  restriction_nodes_.push_back(RestrictionNode{
+      .enumeration_restriction = RestrictionNode::EnumerationRestriction{
+          .offset_begin = offset_begin, .offset_end = offset_end}});
   return true;
 }
 
@@ -1081,9 +1081,9 @@
     return false;
   }
   schema_node->extra = static_cast<int>(restriction_nodes_.size());
-  restriction_nodes_.emplace_back();
-  restriction_nodes_.back().ranged_restriction.max_value = max_value;
-  restriction_nodes_.back().ranged_restriction.min_value = min_value;
+  restriction_nodes_.push_back(
+      RestrictionNode{.ranged_restriction = RestrictionNode::RangedRestriction{
+                          .max_value = max_value, .min_value = min_value}});
   return true;
 }
 
@@ -1105,10 +1105,9 @@
   strings_.push_back(*pattern);
   string_enums_.push_back(strings_.back().c_str());
   schema_node->extra = static_cast<int>(restriction_nodes_.size());
-  restriction_nodes_.emplace_back();
-  restriction_nodes_.back().string_pattern_restriction.pattern_index = index;
-  restriction_nodes_.back().string_pattern_restriction.pattern_index_backup =
-      index;
+  restriction_nodes_.push_back(RestrictionNode{
+      .string_pattern_restriction = RestrictionNode::StringPatternRestriction{
+          .pattern_index = index, .pattern_index_backup = index}});
   return true;
 }
 
diff --git a/components/policy/resources/webui/BUILD.gn b/components/policy/resources/webui/BUILD.gn
index 2442e16..4a6e024 100644
--- a/components/policy/resources/webui/BUILD.gn
+++ b/components/policy/resources/webui/BUILD.gn
@@ -16,7 +16,7 @@
   "policy.html",
 ]
 
-if (is_android) {
+if (is_android || is_ios) {
   static_files += [ "logs/policy_logs.html" ]
 }
 
@@ -45,7 +45,7 @@
   "policy.js",
 ]
 
-if (is_android) {
+if (is_android || is_ios) {
   non_web_component_files += [
     "logs/types.ts",
     "logs/policy_logs.ts",
diff --git a/components/policy/resources/webui/logs/policy_logs.ts b/components/policy/resources/webui/logs/policy_logs.ts
index ab0f1e6d..e6dfb00 100644
--- a/components/policy/resources/webui/logs/policy_logs.ts
+++ b/components/policy/resources/webui/logs/policy_logs.ts
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// <if expr="is_ios">
+import 'chrome://resources/js/ios/web_ui.js';
+// </if>
+
+
 import '../strings.m.js';
 
 import {sendWithPromise} from 'chrome://resources/js/cr.js';
@@ -29,10 +34,14 @@
 }
 
 function displayList() {
-  logs = logs;
-
   const logMessageContainer = getRequiredElement('logs-container');
-  logMessageContainer.innerHTML = window.trustedTypes!.emptyHTML;
+
+  // TrustedTypes is not supported on iOS
+  if (window.trustedTypes) {
+    logMessageContainer.innerHTML = window.trustedTypes!.emptyHTML;
+  } else {
+    logMessageContainer.innerHTML = '';
+  }
   logs.forEach(log => {
     const logMessage = document.createElement('li');
     logMessage.textContent = log.message;
@@ -81,4 +90,4 @@
       .addEventListener('click', fetchLogsAndDisplay);
 }
 
-document.addEventListener('DOMContentLoaded', initialize);
\ No newline at end of file
+document.addEventListener('DOMContentLoaded', initialize);
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 6535cc3..d7fb08ce 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -399,7 +399,9 @@
   if args.cloud_policy_proto_path:
     GenerateFile(args.cloud_policy_proto_path, _WriteCloudPolicyProtobuf)
   if args.chrome_settings_proto_path:
-    GenerateFile(args.chrome_settings_proto_path, _WriteChromeSettingsProtobuf)
+    GenerateFile(args.chrome_settings_proto_path,
+                 _WriteChromeSettingsProtobuf,
+                 sorted=True)
 
   if target_platform == 'android' and args.app_restrictions_path:
     GenerateFile(args.app_restrictions_path, _WriteAppRestrictions, xml=True)
diff --git a/components/prefs/json_pref_store_unittest.cc b/components/prefs/json_pref_store_unittest.cc
index 3f770a4a..2be6b46 100644
--- a/components/prefs/json_pref_store_unittest.cc
+++ b/components/prefs/json_pref_store_unittest.cc
@@ -224,8 +224,7 @@
 // Test fallback behavior for an invalid file.
 TEST_P(JsonPrefStoreTest, InvalidFile) {
   base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
-  ASSERT_LT(0, base::WriteFile(invalid_file, kInvalidJson,
-                               std::size(kInvalidJson) - 1));
+  ASSERT_TRUE(base::WriteFile(invalid_file, kInvalidJson));
 
   auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file);
   EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
@@ -313,8 +312,7 @@
 
 TEST_P(JsonPrefStoreTest, Basic) {
   base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0,
-            base::WriteFile(input_file, kReadJson, std::size(kReadJson) - 1));
+  ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
 
   // Test that the persistent value can be loaded.
   ASSERT_TRUE(PathExists(input_file));
@@ -340,8 +338,7 @@
 
 TEST_P(JsonPrefStoreTest, BasicAsync) {
   base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0,
-            base::WriteFile(input_file, kReadJson, std::size(kReadJson) - 1));
+  ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
 
   // Test that the persistent value can be loaded.
   auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
@@ -448,8 +445,7 @@
 
 TEST_P(JsonPrefStoreTest, ReadWithInterceptor) {
   base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0,
-            base::WriteFile(input_file, kReadJson, std::size(kReadJson) - 1));
+  ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
 
   std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
       new InterceptingPrefFilter());
@@ -490,8 +486,7 @@
 
 TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
   base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0,
-            base::WriteFile(input_file, kReadJson, std::size(kReadJson) - 1));
+  ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
 
   std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
       new InterceptingPrefFilter());
@@ -884,8 +879,7 @@
 
 TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
   base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
-  ASSERT_LT(0,
-            base::WriteFile(input_file, kReadJson, std::size(kReadJson) - 1));
+  ASSERT_TRUE(base::WriteFile(input_file, kReadJson));
 
   std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
       new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair()));
diff --git a/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.h b/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.h
index 1135a3e..12add8f 100644
--- a/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.h
+++ b/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.h
@@ -28,6 +28,7 @@
   // ImmersiveModeController overrides
   void Enable() override;
   void UpdateToolbarVisibility(mojom::ToolbarVisibilityStyle style) override;
+  void OnTopViewBoundsChanged(const gfx::Rect& bounds) override;
   void RevealLock() override;
   void RevealUnlock() override;
   void TitlebarLock() override;
diff --git a/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm b/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm
index 6e23e95..7f87af0 100644
--- a/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm
+++ b/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm
@@ -42,22 +42,23 @@
                               overlay_window,
                               std::move(callback)),
       tab_window_(tab_window) {
-  if (@available(macOS 11.0, *)) {
-    // TODO(https://crbug.com/1414521): Support macOS versions older than
-    // macOS 11.
-    browser_window.toolbarStyle = NSWindowToolbarStyleUnifiedCompact;
-  }
   browser_window.titleVisibility = NSWindowTitleHidden;
   browser_window.toolbar = [[[NSToolbar alloc] init] autorelease];
 
   tab_titlebar_view_controller_.reset([[TabTitlebarViewController alloc] init]);
   tab_titlebar_view_controller_.get().view =
       [[[NSView alloc] init] autorelease];
+
+  // The view is pinned to the opposite side of the traffic lights. A view long
+  // enough is able to paint underneath the traffic lights. This also works with
+  // RTL setups.
   tab_titlebar_view_controller_.get().layoutAttribute =
-      (browser_window.windowTitlebarLayoutDirection ==
-       NSUserInterfaceLayoutDirectionRightToLeft)
-          ? NSLayoutAttributeRight
-          : NSLayoutAttributeLeft;
+      NSLayoutAttributeTrailing;
+
+  // TODO(https://crbug.com/1414521): Fix window tab parent. Currently the tab
+  // window is parented to the browser window. Instead it should be parented to
+  // the AppKit fullscreen window as a sibling of the overlay window. This will
+  // involve some changes to ImmersiveModeDelegate.
 }
 
 ImmersiveModeTabbedController::~ImmersiveModeTabbedController() {
@@ -105,11 +106,21 @@
       TitlebarHide();
       break;
     case mojom::ToolbarVisibilityStyle::kNone:
-      browser_window().toolbar = nil;
+      TitlebarHide();
       break;
   }
 }
 
+void ImmersiveModeTabbedController::OnTopViewBoundsChanged(
+    const gfx::Rect& bounds) {
+  ImmersiveModeController::OnTopViewBoundsChanged(bounds);
+  NSRect frame = NSRectFromCGRect(bounds.ToCGRect());
+  [tab_titlebar_view_controller_.get().view
+      setFrameSize:NSMakeSize(frame.size.width,
+                              tab_titlebar_view_controller_.get()
+                                  .view.frame.size.height)];
+}
+
 void ImmersiveModeTabbedController::RevealLock() {
   ImmersiveModeController::RevealLock();
   TitlebarReveal();
@@ -130,7 +141,7 @@
   if (@available(macOS 10.15, *)) {
     browser_window().titlebarHeight = tab_window_.frame.size.height - 1;
   }
-  browser_window().toolbar = [[[NSToolbar alloc] init] autorelease];
+  browser_window().toolbar.visible = YES;
   if (@available(macOS 10.15, *)) {
     browser_window().titlebarHeight = tab_window_.frame.size.height;
   }
@@ -142,7 +153,7 @@
   if (@available(macOS 10.15, *)) {
     browser_window().titlebarHeight = tab_window_.frame.size.height - 1;
   }
-  browser_window().toolbar = nil;
+  browser_window().toolbar.visible = NO;
   if (@available(macOS 10.15, *)) {
     browser_window().titlebarHeight = tab_window_.frame.size.height;
   }
diff --git a/components/sessions/core/command_storage_backend_unittest.cc b/components/sessions/core/command_storage_backend_unittest.cc
index d3b477c..f9e3612 100644
--- a/components/sessions/core/command_storage_backend_unittest.cc
+++ b/components/sessions/core/command_storage_backend_unittest.cc
@@ -587,7 +587,7 @@
       base::FilePath(kSessionsDirectory)
           .Append(FILE_PATH_LITERAL("Session_13235178308836991")));
   ASSERT_TRUE(base::CreateDirectory(prev_path.DirName()));
-  ASSERT_EQ(0, base::WriteFile(prev_path, "", 0));
+  ASSERT_TRUE(base::WriteFile(prev_path, ""));
 
   scoped_refptr<CommandStorageBackend> backend = CreateBackendWithRestoreType();
   auto last_session_info = GetLastSessionInfo(backend.get());
@@ -607,9 +607,9 @@
       sessions_dir.Append(FILE_PATH_LITERAL("Session_13235178308548874"));
   const auto old_path_2 = sessions_dir.Append(FILE_PATH_LITERAL("Session_0"));
   ASSERT_TRUE(base::CreateDirectory(prev_path.DirName()));
-  ASSERT_EQ(0, base::WriteFile(prev_path, "", 0));
-  ASSERT_EQ(0, base::WriteFile(old_path_1, "", 0));
-  ASSERT_EQ(0, base::WriteFile(old_path_2, "", 0));
+  ASSERT_TRUE(base::WriteFile(prev_path, ""));
+  ASSERT_TRUE(base::WriteFile(old_path_1, ""));
+  ASSERT_TRUE(base::WriteFile(old_path_2, ""));
 
   scoped_refptr<CommandStorageBackend> backend = CreateBackendWithRestoreType();
   auto last_session_info = GetLastSessionInfo(backend.get());
@@ -624,7 +624,7 @@
       restore_path().Append(base::FilePath(kSessionsDirectory)
                                 .Append(FILE_PATH_LITERAL("Session_invalid")));
   ASSERT_TRUE(base::CreateDirectory(prev_path.DirName()));
-  ASSERT_EQ(0, base::WriteFile(prev_path, "", 0));
+  ASSERT_TRUE(base::WriteFile(prev_path, ""));
 
   scoped_refptr<CommandStorageBackend> backend = CreateBackendWithRestoreType();
   auto last_session_info = GetLastSessionInfo(backend.get());
@@ -639,7 +639,7 @@
   const auto last_session =
       sessions_dir.Append(FILE_PATH_LITERAL("Session_13235178308548874"));
   ASSERT_TRUE(base::CreateDirectory(last_session.DirName()));
-  ASSERT_EQ(0, base::WriteFile(last_session, "", 0));
+  ASSERT_TRUE(base::WriteFile(last_session, ""));
 
   scoped_refptr<CommandStorageBackend> backend = CreateBackendWithRestoreType();
   char buffer[1];
@@ -652,16 +652,16 @@
   EXPECT_TRUE(CommandStorageBackend::GetSessionFilePaths(
                   file_path(), CommandStorageManager::kOther)
                   .empty());
-  ASSERT_EQ(0, base::WriteFile(file_path(), "", 0));
+  ASSERT_TRUE(base::WriteFile(file_path(), ""));
   // Not a valid name, as doesn't contain timestamp separator.
-  ASSERT_EQ(0, base::WriteFile(file_path().DirName().AppendASCII("Session 123"),
-                               "", 0));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Session 123"), ""));
   // Valid name.
-  ASSERT_EQ(0, base::WriteFile(file_path().DirName().AppendASCII("Session_124"),
-                               "", 0));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Session_124"), ""));
   // Valid name, but should not be returned as beginning doesn't match.
-  ASSERT_EQ(
-      0, base::WriteFile(file_path().DirName().AppendASCII("Foo_125"), "", 0));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Foo_125"), ""));
   auto paths = CommandStorageBackend::GetSessionFilePaths(
       file_path(), CommandStorageManager::kOther);
   ASSERT_EQ(1u, paths.size());
@@ -674,14 +674,14 @@
 }
 
 TEST_F(CommandStorageBackendTest, GetSessionFilesAreSortedByReverseTimestamp) {
-  ASSERT_EQ(0, base::WriteFile(file_path().DirName().AppendASCII("Session_130"),
-                               "", 0));
-  ASSERT_EQ(0, base::WriteFile(file_path().DirName().AppendASCII("Session_120"),
-                               "", 0));
-  ASSERT_EQ(0, base::WriteFile(file_path().DirName().AppendASCII("Session_125"),
-                               "", 0));
-  ASSERT_EQ(0, base::WriteFile(file_path().DirName().AppendASCII("Session_128"),
-                               "", 0));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Session_130"), ""));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Session_120"), ""));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Session_125"), ""));
+  ASSERT_TRUE(
+      base::WriteFile(file_path().DirName().AppendASCII("Session_128"), ""));
   auto paths = GetSessionFilePathsSortedByReverseTimestamp();
   ASSERT_EQ(4u, paths.size());
   EXPECT_EQ("Session_130", paths[0].BaseName().MaybeAsASCII());
diff --git a/components/shared_highlighting/ios/parsing_utils.mm b/components/shared_highlighting/ios/parsing_utils.mm
index fc21709..4970ac3f 100644
--- a/components/shared_highlighting/ios/parsing_utils.mm
+++ b/components/shared_highlighting/ios/parsing_utils.mm
@@ -28,21 +28,17 @@
     return absl::nullopt;
   }
 
-  const base::Value* xValue =
-      value->FindKeyOfType("x", base::Value::Type::DOUBLE);
-  const base::Value* yValue =
-      value->FindKeyOfType("y", base::Value::Type::DOUBLE);
-  const base::Value* widthValue =
-      value->FindKeyOfType("width", base::Value::Type::DOUBLE);
-  const base::Value* heightValue =
-      value->FindKeyOfType("height", base::Value::Type::DOUBLE);
+  const base::Value::Dict& dict = value->GetDict();
+  absl::optional<double> xValue = dict.FindDouble("x");
+  absl::optional<double> yValue = dict.FindDouble("y");
+  absl::optional<double> widthValue = dict.FindDouble("width");
+  absl::optional<double> heightValue = dict.FindDouble("height");
 
   if (!xValue || !yValue || !widthValue || !heightValue) {
     return absl::nullopt;
   }
 
-  return CGRectMake(xValue->GetDouble(), yValue->GetDouble(),
-                    widthValue->GetDouble(), heightValue->GetDouble());
+  return CGRectMake(*xValue, *yValue, *widthValue, *heightValue);
 }
 
 absl::optional<GURL> ParseURL(const std::string* url_value) {
diff --git a/components/storage_monitor/image_capture_device_manager_unittest.mm b/components/storage_monitor/image_capture_device_manager_unittest.mm
index 3ed2c58..0d99e1e 100644
--- a/components/storage_monitor/image_capture_device_manager_unittest.mm
+++ b/components/storage_monitor/image_capture_device_manager_unittest.mm
@@ -97,9 +97,7 @@
   // filename. Do that here to require a rename.
   saveAsFilename += ".jpg";
   base::FilePath toBeSaved = saveDir.Append(saveAsFilename);
-  ASSERT_EQ(static_cast<int>(strlen(kTestFileContents)),
-            base::WriteFile(toBeSaved, kTestFileContents,
-                            strlen(kTestFileContents)));
+  ASSERT_TRUE(base::WriteFile(toBeSaved, kTestFileContents));
 
   NSMutableDictionary* returnOptions =
       [NSMutableDictionary dictionaryWithDictionary:options];
diff --git a/components/subresource_filter/content/browser/ruleset_publisher_impl_unittest.cc b/components/subresource_filter/content/browser/ruleset_publisher_impl_unittest.cc
index 724eefd..9dc01f7 100644
--- a/components/subresource_filter/content/browser/ruleset_publisher_impl_unittest.cc
+++ b/components/subresource_filter/content/browser/ruleset_publisher_impl_unittest.cc
@@ -137,8 +137,7 @@
 TEST_F(SubresourceFilterRulesetPublisherImplTest,
        PublishedRuleset_IsDistributedToExistingAndNewRenderers) {
   const char kTestFileContents[] = "foobar";
-  base::WriteFile(scoped_temp_file(), kTestFileContents,
-                  strlen(kTestFileContents));
+  base::WriteFile(scoped_temp_file(), kTestFileContents);
 
   RulesetFilePtr file(
       new base::File(scoped_temp_file(),
diff --git a/components/subresource_filter/content/browser/ruleset_service.cc b/components/subresource_filter/content/browser/ruleset_service.cc
index 0ea1935..c0ebdf4 100644
--- a/components/subresource_filter/content/browser/ruleset_service.cc
+++ b/components/subresource_filter/content/browser/ruleset_service.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/check_op.h"
+#include "base/containers/span.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -15,6 +16,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -77,7 +79,7 @@
   SentinelFile& operator=(const SentinelFile&) = delete;
 
   bool IsPresent() { return base::PathExists(path_); }
-  bool Create() { return base::WriteFile(path_, nullptr, 0) == 0; }
+  bool Create() { return base::WriteFile(path_, base::StringPiece()); }
   bool Remove() { return base::DeleteFile(path_); }
 
  private:
@@ -378,11 +380,9 @@
   }
 
   static_assert(sizeof(uint8_t) == sizeof(char), "Expected char = byte.");
-  const int data_size_in_chars = base::checked_cast<int>(indexed_ruleset_size);
-  if (base::WriteFile(
+  if (!base::WriteFile(
           IndexedRulesetLocator::GetRulesetDataFilePath(scratch_dir.GetPath()),
-          reinterpret_cast<const char*>(indexed_ruleset_data),
-          data_size_in_chars) != data_size_in_chars) {
+          base::make_span(indexed_ruleset_data, indexed_ruleset_size))) {
     return IndexAndWriteRulesetResult::FAILED_WRITING_RULESET_DATA;
   }
 
diff --git a/components/subresource_filter/content/browser/ruleset_service_unittest.cc b/components/subresource_filter/content/browser/ruleset_service_unittest.cc
index 51abccc..c8b8daf 100644
--- a/components/subresource_filter/content/browser/ruleset_service_unittest.cc
+++ b/components/subresource_filter/content/browser/ruleset_service_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -252,9 +253,7 @@
                              base::FilePath* path) {
     ASSERT_NO_FATAL_FAILURE(
         test_ruleset_creator()->GetUniqueTemporaryPath(path));
-    ASSERT_EQ(static_cast<int>(contents.size()),
-              base::WriteFile(*path, contents.data(),
-                              static_cast<int>(contents.size())));
+    ASSERT_TRUE(base::WriteFile(*path, contents));
   }
 
   void IndexAndStoreAndPublishUpdatedRuleset(
@@ -598,13 +597,13 @@
   WriteRuleset(test_ruleset_1(), legacy_format_content_version_1);
   WriteRuleset(test_ruleset_2(), legacy_format_content_version_2);
   base::WriteFile(GetExpectedSentinelFilePath(legacy_format_content_version_2),
-                  nullptr, 0);
+                  base::StringPiece());
 
   WriteRuleset(test_ruleset_1(), current_format_content_version_1);
   WriteRuleset(test_ruleset_2(), current_format_content_version_2);
   WriteRuleset(test_ruleset_3(), current_format_content_version_3);
   base::WriteFile(GetExpectedSentinelFilePath(current_format_content_version_3),
-                  nullptr, 0);
+                  base::StringPiece());
 
   DeleteObsoleteRulesets(base_dir(), current_format_content_version_2);
 
diff --git a/components/subresource_filter/core/common/test_ruleset_creator.cc b/components/subresource_filter/core/common/test_ruleset_creator.cc
index a02f0a0..59cebf4 100644
--- a/components/subresource_filter/core/common/test_ruleset_creator.cc
+++ b/components/subresource_filter/core/common/test_ruleset_creator.cc
@@ -31,11 +31,7 @@
 void WriteRulesetContents(const std::vector<uint8_t>& contents,
                           base::FilePath path) {
   base::ScopedAllowBlockingForTesting allow_blocking;
-  int ruleset_size_as_int = base::checked_cast<int>(contents.size());
-  int num_bytes_written =
-      base::WriteFile(path, reinterpret_cast<const char*>(contents.data()),
-                      ruleset_size_as_int);
-  ASSERT_EQ(ruleset_size_as_int, num_bytes_written);
+  ASSERT_TRUE(base::WriteFile(path, contents));
 }
 
 std::vector<uint8_t> SerializeUnindexedRulesetWithMultipleRules(
diff --git a/components/subresource_filter/tools/indexing_tool.cc b/components/subresource_filter/tools/indexing_tool.cc
index 5b0631e..e5c7b7b 100644
--- a/components/subresource_filter/tools/indexing_tool.cc
+++ b/components/subresource_filter/tools/indexing_tool.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/containers/span.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/numerics/safe_conversions.h"
@@ -45,8 +46,7 @@
 
   indexer.Finish();
 
-  base::WriteFile(indexed_path, reinterpret_cast<const char*>(indexer.data()),
-                  base::checked_cast<int>(indexer.size()));
+  base::WriteFile(indexed_path, base::make_span(indexer));
 
   if (out_checksum)
     *out_checksum = indexer.GetChecksum();
@@ -69,7 +69,7 @@
   std::string version = base::StringPrintf(
       version_format, content_version.c_str(),
       subresource_filter::RulesetIndexer::kIndexedFormatVersion, checksum);
-  base::WriteFile(path, version.data(), version.size());
+  base::WriteFile(path, version);
 }
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/tools/indexing_tool_unittest.cc b/components/subresource_filter/tools/indexing_tool_unittest.cc
index db23797..1482da5 100644
--- a/components/subresource_filter/tools/indexing_tool_unittest.cc
+++ b/components/subresource_filter/tools/indexing_tool_unittest.cc
@@ -70,8 +70,7 @@
     // Write the test unindexed data to a file.
     const std::vector<uint8_t>& unindexed_data =
         test_ruleset_pair_.unindexed.contents;
-    base::WriteFile(path, reinterpret_cast<const char*>(unindexed_data.data()),
-                    base::checked_cast<int>(unindexed_data.size()));
+    base::WriteFile(path, unindexed_data);
   }
 
   int file_count_ = 0;
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc b/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc
index c043688..e37c1d2c 100644
--- a/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc
+++ b/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc
@@ -329,8 +329,7 @@
 
   // Output all the rules to the |source_ruleset| file.
   std::string joined_rules = base::JoinString(text_rules, "\n");
-  base::WriteFile(source_ruleset.ruleset_path(), joined_rules.data(),
-                  joined_rules.size());
+  base::WriteFile(source_ruleset.ruleset_path(), joined_rules);
 
   // Filter out the rules with parse errors, and save the rest to |contents|.
   TestRulesetContents contents;
diff --git a/components/upload_list/combining_upload_list_unittest.cc b/components/upload_list/combining_upload_list_unittest.cc
index 229e316..ee116be 100644
--- a/components/upload_list/combining_upload_list_unittest.cc
+++ b/components/upload_list/combining_upload_list_unittest.cc
@@ -53,22 +53,19 @@
 1614008000,ddee0008
 1614012000,ddee0012
   )";
-  ASSERT_GT(base::WriteFile(first_log_path(), kFirstList, strlen(kFirstList)),
-            0);
+  ASSERT_TRUE(base::WriteFile(first_log_path(), kFirstList));
 
   constexpr char kSecondList[] = R"(
 {"upload_time":"1614002000","upload_id":"ddee0002"}
 {"upload_time":"1614006000","upload_id":"ddee0006"}
 {"upload_time":"1614010000","upload_id":"ddee0010"}
   )";
-  ASSERT_GT(
-      base::WriteFile(second_log_path(), kSecondList, strlen(kSecondList)), 0);
+  ASSERT_TRUE(base::WriteFile(second_log_path(), kSecondList));
 
   constexpr char kThirdList[] = R"(
 {"upload_time":"1614014000","upload_id":"ddee0014"}
   )";
-  ASSERT_GT(base::WriteFile(third_log_path(), kThirdList, strlen(kThirdList)),
-            0);
+  ASSERT_TRUE(base::WriteFile(third_log_path(), kThirdList));
 
   std::vector<scoped_refptr<UploadList>> sublists = {
       first_reader_, second_reader_, third_reader_};
@@ -138,25 +135,19 @@
 {"capture_time":"1614004000","upload_id":"ddee0004","upload_time":"1614999999"}
 {"capture_time":"1614007000","upload_id":"ddee0007","upload_time":"1600000000"}
   )";
-  ASSERT_GT(base::WriteFile(first_log_path(), kUploadAndCaptureTimes,
-                            strlen(kUploadAndCaptureTimes)),
-            0);
+  ASSERT_TRUE(base::WriteFile(first_log_path(), kUploadAndCaptureTimes));
   constexpr char kJustCaptureTimes[] = R"(
 {"capture_time":"1614002000","upload_id":"ddee0002"}
 {"capture_time":"1614005000","upload_id":"ddee0005"}
 {"capture_time":"1614008000","upload_id":"ddee0008"}
   )";
-  ASSERT_GT(base::WriteFile(second_log_path(), kJustCaptureTimes,
-                            strlen(kJustCaptureTimes)),
-            0);
+  ASSERT_TRUE(base::WriteFile(second_log_path(), kJustCaptureTimes));
   constexpr char kJustUploadTimes[] = R"(
 {"upload_time":"1614003000","upload_id":"ddee0003"}
 {"upload_time":"1614006000","upload_id":"ddee0006"}
 {"upload_time":"1614009000","upload_id":"ddee0009"}
   )";
-  ASSERT_GT(base::WriteFile(third_log_path(), kJustUploadTimes,
-                            strlen(kJustUploadTimes)),
-            0);
+  ASSERT_TRUE(base::WriteFile(third_log_path(), kJustUploadTimes));
 
   std::vector<scoped_refptr<UploadList>> sublists = {
       first_reader_, second_reader_, third_reader_};
@@ -222,25 +213,19 @@
 {"capture_time":"1614004000","upload_id":"ddee0004","upload_time":"1614999999"}
 {"capture_time":"1614007000","upload_id":"ddee0007","upload_time":"1600000000"}
 )";
-  ASSERT_GT(base::WriteFile(first_log_path(), kUploadAndCaptureTimes,
-                            strlen(kUploadAndCaptureTimes)),
-            0);
+  ASSERT_TRUE(base::WriteFile(first_log_path(), kUploadAndCaptureTimes));
   constexpr char kJustCaptureTimes[] = R"(
 {"capture_time":"1614002000","upload_id":"ddee0002"}
 {"capture_time":"1614005000","upload_id":"ddee0005"}
 {"capture_time":"1614008000","upload_id":"ddee0008"}
 )";
-  ASSERT_GT(base::WriteFile(second_log_path(), kJustCaptureTimes,
-                            strlen(kJustCaptureTimes)),
-            0);
+  ASSERT_TRUE(base::WriteFile(second_log_path(), kJustCaptureTimes));
   constexpr char kJustUploadTimes[] = R"(
 {"upload_time":"1614003000","upload_id":"ddee0003"}
 {"upload_time":"1614006000","upload_id":"ddee0006"}
 {"upload_time":"1614009000","upload_id":"ddee0009"}
 )";
-  ASSERT_GT(base::WriteFile(third_log_path(), kJustUploadTimes,
-                            strlen(kJustUploadTimes)),
-            0);
+  ASSERT_TRUE(base::WriteFile(third_log_path(), kJustUploadTimes));
 
   std::vector<scoped_refptr<UploadList>> sublists = {
       first_reader_, second_reader_, third_reader_};
diff --git a/components/upload_list/text_log_upload_list.cc b/components/upload_list/text_log_upload_list.cc
index 1c698df4..151d15d 100644
--- a/components/upload_list/text_log_upload_list.cc
+++ b/components/upload_list/text_log_upload_list.cc
@@ -238,8 +238,7 @@
   if (new_contents.size() == 0) {
     base::DeleteFile(upload_log_path_);
   } else {
-    base::WriteFile(upload_log_path_, new_contents.c_str(),
-                    new_contents.size());
+    base::WriteFile(upload_log_path_, new_contents);
   }
 }
 
diff --git a/components/upload_list/text_log_upload_list_unittest.cc b/components/upload_list/text_log_upload_list_unittest.cc
index 3764af80..4c883c259 100644
--- a/components/upload_list/text_log_upload_list_unittest.cc
+++ b/components/upload_list/text_log_upload_list_unittest.cc
@@ -39,9 +39,7 @@
 
  protected:
   void WriteUploadLog(const std::string& log_data) {
-    ASSERT_GT(base::WriteFile(log_path(), log_data.c_str(),
-                              static_cast<int>(log_data.size())),
-              0);
+    ASSERT_TRUE(base::WriteFile(log_path(), log_data));
   }
 
   base::FilePath log_path() {
diff --git a/components/visitedlink/browser/visitedlink_event_listener.cc b/components/visitedlink/browser/visitedlink_event_listener.cc
index 7808ff0..641f2f0e 100644
--- a/components/visitedlink/browser/visitedlink_event_listener.cc
+++ b/components/visitedlink/browser/visitedlink_event_listener.cc
@@ -122,8 +122,6 @@
     content::BrowserContext* browser_context)
     : coalesce_timer_(&default_coalesce_timer_),
       browser_context_(browser_context) {
-  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
-                 content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
                  content::NotificationService::AllBrowserContextsAndSources());
 }
@@ -199,6 +197,18 @@
 
   updaters_[rph->GetID()] = std::make_unique<VisitedLinkUpdater>(rph->GetID());
   updaters_[rph->GetID()]->SendVisitedLinkTable(&table_region_);
+
+  if (!host_observation_.IsObservingSource(rph)) {
+    host_observation_.AddObservation(rph);
+  }
+}
+
+void VisitedLinkEventListener::RenderProcessHostDestroyed(
+    content::RenderProcessHost* host) {
+  if (host_observation_.IsObservingSource(host)) {
+    updaters_.erase(host->GetID());
+    host_observation_.RemoveObservation(host);
+  }
 }
 
 void VisitedLinkEventListener::Observe(
@@ -206,14 +216,6 @@
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
   switch (type) {
-    case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
-      content::RenderProcessHost* process =
-          content::Source<content::RenderProcessHost>(source).ptr();
-      if (updaters_.count(process->GetID())) {
-        updaters_.erase(process->GetID());
-      }
-      break;
-    }
     case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
       RenderWidgetHost* widget =
           content::Source<RenderWidgetHost>(source).ptr();
diff --git a/components/visitedlink/browser/visitedlink_event_listener.h b/components/visitedlink/browser/visitedlink_event_listener.h
index 3df9903..b148c0c 100644
--- a/components/visitedlink/browser/visitedlink_event_listener.h
+++ b/components/visitedlink/browser/visitedlink_event_listener.h
@@ -10,11 +10,13 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/read_only_shared_memory_region.h"
+#include "base/scoped_multi_source_observation.h"
 #include "base/timer/timer.h"
 #include "components/visitedlink/browser/visitedlink_writer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/render_process_host_creation_observer.h"
+#include "content/public/browser/render_process_host_observer.h"
 
 namespace content {
 class BrowserContext;
@@ -30,6 +32,7 @@
 class VisitedLinkEventListener
     : public VisitedLinkWriter::Listener,
       public content::NotificationObserver,
+      public content::RenderProcessHostObserver,
       public content::RenderProcessHostCreationObserver {
  public:
   explicit VisitedLinkEventListener(content::BrowserContext* browser_context);
@@ -50,6 +53,9 @@
   // content::RenderProcessHostCreationObserver:
   void OnRenderProcessHostCreated(content::RenderProcessHost* rph) override;
 
+  // content::RenderProcessHostObserver:
+  void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
+
  private:
   void CommitVisitedLinks();
 
@@ -67,11 +73,14 @@
   raw_ptr<base::OneShotTimer> coalesce_timer_;
   VisitedLinkCommon::Fingerprints pending_visited_links_;
 
+  base::ScopedMultiSourceObservation<content::RenderProcessHost,
+                                     content::RenderProcessHostObserver>
+      host_observation_{this};
+
   content::NotificationRegistrar registrar_;
 
   // Map between renderer child ids and their VisitedLinkUpdater.
-  typedef std::map<int, std::unique_ptr<VisitedLinkUpdater>> Updaters;
-  Updaters updaters_;
+  std::map<int, std::unique_ptr<VisitedLinkUpdater>> updaters_;
 
   base::ReadOnlySharedMemoryRegion table_region_;
 
diff --git a/components/visitedlink/browser/visitedlink_writer.cc b/components/visitedlink/browser/visitedlink_writer.cc
index 0be6871..ca49f06e 100644
--- a/components/visitedlink/browser/visitedlink_writer.cc
+++ b/components/visitedlink/browser/visitedlink_writer.cc
@@ -1104,7 +1104,7 @@
     WriteToFile(scoped_file_holder_.get(),
                 first_hash * sizeof(Fingerprint) + kFileHeaderSize,
                 &hash_table_[first_hash],
-                (table_length_ - first_hash + 1) * sizeof(Fingerprint));
+                (table_length_ - first_hash) * sizeof(Fingerprint));
 
     // Now do 0->last_lash.
     WriteToFile(scoped_file_holder_.get(), kFileHeaderSize, hash_table_,
diff --git a/components/visitedlink/browser/visitedlink_writer.h b/components/visitedlink/browser/visitedlink_writer.h
index 03e3a9d0..31981ba 100644
--- a/components/visitedlink/browser/visitedlink_writer.h
+++ b/components/visitedlink/browser/visitedlink_writer.h
@@ -177,6 +177,7 @@
   FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, Delete);
   FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, BigDelete);
   FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, BigImport);
+  FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, HashRangeWraparound);
 
   // Keeps the result of loading the table from the database file to the UI
   // thread.
diff --git a/components/visitedlink/test/visitedlink_unittest.cc b/components/visitedlink/test/visitedlink_unittest.cc
index 79a80ef..a71483d 100644
--- a/components/visitedlink/test/visitedlink_unittest.cc
+++ b/components/visitedlink/test/visitedlink_unittest.cc
@@ -26,10 +26,9 @@
 #include "components/visitedlink/browser/visitedlink_event_listener.h"
 #include "components/visitedlink/browser/visitedlink_writer.h"
 #include "components/visitedlink/common/visitedlink.mojom.h"
+#include "components/visitedlink/common/visitedlink_common.h"
 #include "components/visitedlink/renderer/visitedlink_reader.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
@@ -546,6 +545,39 @@
   EXPECT_EQ(1, listener->completely_reset_count());
 }
 
+TEST_F(VisitedLinkTest, HashRangeWraparound) {
+  ASSERT_TRUE(InitVisited(0, true, true));
+
+  // Create two fingerprints that, when added, will create a wraparound hash
+  // range.
+  const VisitedLinkCommon::Fingerprint kFingerprint0 =
+      writer_->DefaultTableSize() - 1;
+  const VisitedLinkCommon::Fingerprint kFingerprint1 = kFingerprint0 + 1;
+
+  // Add the two fingerprints.
+  const VisitedLinkCommon::Hash hash0 =
+      writer_->AddFingerprint(kFingerprint0, false);
+  const VisitedLinkCommon::Hash hash1 =
+      writer_->AddFingerprint(kFingerprint1, false);
+
+  // Verify the hashes form a range that wraps around.
+  EXPECT_EQ(hash0, VisitedLinkCommon::Hash(writer_->DefaultTableSize() - 1));
+  EXPECT_EQ(hash1, 0);
+
+  // Write the database to file.
+  writer_->WriteUsedItemCountToFile();
+  writer_->WriteHashRangeToFile(hash0, hash1);
+
+  // Close and reopen the database.
+  ClearDB();
+  ASSERT_TRUE(InitVisited(0, true, true));
+
+  // Verify database contents.
+  ASSERT_EQ(writer_->GetUsedCount(), 2);
+  ASSERT_TRUE(writer_->IsVisited(kFingerprint0));
+  ASSERT_TRUE(writer_->IsVisited(kFingerprint1));
+}
+
 class VisitCountingContext : public mojom::VisitedLinkNotificationSink {
  public:
   VisitCountingContext()
@@ -636,13 +668,6 @@
       delete;
   VisitRelayingRenderProcessHost& operator=(
       const VisitRelayingRenderProcessHost&) = delete;
-
-  ~VisitRelayingRenderProcessHost() override {
-    content::NotificationService::current()->Notify(
-        content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
-        content::Source<content::RenderProcessHost>(this),
-        content::NotificationService::NoDetails());
-  }
 };
 
 class VisitedLinkRenderProcessHostFactory
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index d05a7e6..473b1ada 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1193,8 +1193,6 @@
     "loader/prefetch_url_loader.h",
     "loader/prefetch_url_loader_service.cc",
     "loader/prefetch_url_loader_service.h",
-    "loader/resource_timing_utils.cc",
-    "loader/resource_timing_utils.h",
     "loader/shared_cors_origin_access_list_impl.cc",
     "loader/shared_cors_origin_access_list_impl.h",
     "loader/url_loader_throttles.cc",
diff --git a/content/browser/background_fetch/mock_background_fetch_delegate.cc b/content/browser/background_fetch/mock_background_fetch_delegate.cc
index f241493..f5f33ca 100644
--- a/content/browser/background_fetch/mock_background_fetch_delegate.cc
+++ b/content/browser/background_fetch/mock_background_fetch_delegate.cc
@@ -147,9 +147,7 @@
     CHECK(base::CreateTemporaryFileInDir(temp_directory_.GetPath(),
                                          &response_path));
 
-    CHECK_NE(/* error= */ -1,
-             base::WriteFile(response_path, test_response->data.c_str(),
-                             test_response->data.size()));
+    CHECK(base::WriteFile(response_path, test_response->data));
 
     PostAbortCheckingTask(
         job_unique_id,
diff --git a/content/browser/cache_storage/cache_storage.cc b/content/browser/cache_storage/cache_storage.cc
index fcd718e8..f686806 100644
--- a/content/browser/cache_storage/cache_storage.cc
+++ b/content/browser/cache_storage/cache_storage.cc
@@ -399,8 +399,7 @@
       const std::string& data,
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
       const storage::BucketLocator& bucket_locator) {
-    int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size());
-    if (bytes_written != base::checked_cast<int>(data.size())) {
+    if (!base::WriteFile(tmp_path, data)) {
       base::DeleteFile(tmp_path);
       quota_manager_proxy->NotifyWriteFailed(bucket_locator.storage_key);
       return false;
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc
index b91c420..3083bd8 100644
--- a/content/browser/devtools/devtools_http_handler.cc
+++ b/content/browser/devtools/devtools_http_handler.cc
@@ -301,8 +301,7 @@
           output_directory.Append(kDevToolsActivePortFileName);
       std::string port_target_string = base::StringPrintf(
           "%d\n%s", ip_address->port(), browser_guid.c_str());
-      if (base::WriteFile(path, port_target_string.c_str(),
-                          static_cast<int>(port_target_string.length())) < 0) {
+      if (!base::WriteFile(path, port_target_string)) {
         PLOG(ERROR) << "Error writing DevTools active port to file " << path;
       }
     }
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index d0eacd2..a84e713 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -3235,8 +3235,7 @@
       parameters.pattern_generator_seed, 0, kIntermediateSize);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    ASSERT_EQ(kIntermediateSize, base::WriteFile(intermediate_file_path,
-                                                 output.data(), output.size()));
+    ASSERT_TRUE(base::WriteFile(intermediate_file_path, output));
   }
 
   url_chain.push_back(server_url);
@@ -3291,8 +3290,7 @@
       parameters.pattern_generator_seed + 1, 0, kIntermediateSize);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    ASSERT_EQ(kIntermediateSize, base::WriteFile(intermediate_file_path,
-                                                 output.data(), output.size()));
+    ASSERT_TRUE(base::WriteFile(intermediate_file_path, output));
   }
 
   url_chain.push_back(server_url);
@@ -3348,8 +3346,7 @@
       parameters.pattern_generator_seed, 0, kIntermediateSize);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    ASSERT_EQ(kIntermediateSize, base::WriteFile(intermediate_file_path,
-                                                 output.data(), output.size()));
+    ASSERT_TRUE(base::WriteFile(intermediate_file_path, output));
   }
   // SHA-256 hash of the pattern bytes in buffer.
   static const uint8_t kPartialHash[] = {
@@ -3416,8 +3413,8 @@
   std::vector<char> buffer(kIntermediateSize);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    ASSERT_EQ(kIntermediateSize, base::WriteFile(intermediate_file_path,
-                                                 buffer.data(), buffer.size()));
+    ASSERT_TRUE(base::WriteFile(intermediate_file_path,
+                                {buffer.data(), buffer.size()}));
   }
   // SHA-256 hash of the expected pattern bytes in buffer. This doesn't match
   // the current contents of the intermediate file which should all be 0.
@@ -3501,9 +3498,7 @@
       parameters.pattern_generator_seed, 0, kIntermediateSize - 100);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    ASSERT_EQ(
-        kIntermediateSize - 100,
-        base::WriteFile(intermediate_file_path, output.data(), output.size()));
+    ASSERT_TRUE(base::WriteFile(intermediate_file_path, output));
   }
   url_chain.push_back(server_url);
 
@@ -3577,9 +3572,7 @@
       parameters.pattern_generator_seed, 0, kIntermediateSize + 100);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    ASSERT_EQ(
-        kIntermediateSize + 100,
-        base::WriteFile(intermediate_file_path, output.data(), output.size()));
+    ASSERT_TRUE(base::WriteFile(intermediate_file_path, output));
   }
   url_chain.push_back(server_url);
 
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index 9f0de33..d4a1cf9a 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -1348,7 +1348,7 @@
     EXPECT_TRUE(base::PathExists(first_blob));
     EXPECT_FALSE(base::PathExists(corrupt_blob));
     const char kCorruptData[] = "corrupt";
-    base::WriteFile(corrupt_blob, kCorruptData, sizeof(kCorruptData));
+    base::WriteFile(corrupt_blob, kCorruptData);
   }
 
   SimpleTest(embedded_test_server()->GetURL(test_file));
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index edfda65f9..afc96d4 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -1148,10 +1148,9 @@
   interest_group.priority_signals_overrides = {{{"old1", 1}, {"old2", 2}}};
   interest_group.seller_capabilities.emplace();
   interest_group.seller_capabilities->insert(std::make_pair(
-      kOriginA,
-      blink::InterestGroup::SellerCapabilities::kInterestGroupCounts));
+      kOriginA, blink::SellerCapabilities::kInterestGroupCounts));
   interest_group.all_sellers_capabilities =
-      blink::InterestGroup::SellerCapabilities::kLatencyStats;
+      blink::SellerCapabilities::kLatencyStats;
   interest_group.daily_update_url = kUpdateUrlA;
   interest_group.bidding_url = kBiddingLogicUrlA;
   interest_group.trusted_bidding_signals_url = kTrustedBiddingSignalsUrlA;
@@ -1198,11 +1197,11 @@
             expected_priority_signals_overrides);
 
   EXPECT_EQ(group.all_sellers_capabilities,
-            blink::InterestGroup::SellerCapabilities::kInterestGroupCounts);
+            blink::SellerCapabilities::kInterestGroupCounts);
   ASSERT_TRUE(group.seller_capabilities);
   ASSERT_EQ(group.seller_capabilities->size(), 1u);
   EXPECT_EQ(group.seller_capabilities->at(kOriginA),
-            blink::InterestGroup::SellerCapabilities::kLatencyStats);
+            blink::SellerCapabilities::kLatencyStats);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(group.bidding_url->spec(),
             base::StringPrintf("%s/interest_group/new_bidding_logic.js",
@@ -2015,8 +2014,7 @@
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
   const auto& group = groups[0].interest_group;
-  EXPECT_EQ(group.all_sellers_capabilities,
-            blink::InterestGroup::SellerCapabilitiesType());
+  EXPECT_EQ(group.all_sellers_capabilities, blink::SellerCapabilitiesType());
   EXPECT_FALSE(group.seller_capabilities);
   EXPECT_EQ(group.bidding_url, kBiddingLogicUrlA);
 }
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index a4865c86..02d5d7b 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -1298,8 +1298,9 @@
       blink::AuctionConfig::BuyerTimeouts buyer_cumulative_timeouts;
       buyer_cumulative_timeouts.per_buyer_timeouts.emplace();
       buyer_cumulative_timeouts.per_buyer_timeouts.value()[kBidder1] =
-          base::Milliseconds(12345);
-      buyer_cumulative_timeouts.all_buyers_timeout = base::Milliseconds(23456);
+          kBidder1CumulativeTimeout;
+      buyer_cumulative_timeouts.all_buyers_timeout =
+          kAllBuyersCumulativeTimeout;
       return blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromValue(
           std::move(buyer_cumulative_timeouts));
     }
@@ -1444,11 +1445,10 @@
                        base::Unretained(this)));
   }
 
-  const Result& RunAuctionAndWait(const GURL& seller_decision_logic_url,
-                                  std::vector<StorageInterestGroup> bidders) {
+  void RunAuctionAndWait(const GURL& seller_decision_logic_url,
+                         std::vector<StorageInterestGroup> bidders) {
     StartAuction(seller_decision_logic_url, std::move(bidders));
     auction_run_loop_->Run();
-    return result_;
   }
 
   void OnAuctionComplete(
@@ -1662,20 +1662,23 @@
     StartAuction(kSellerUrl, std::move(bidders));
   }
 
-  const Result& RunStandardAuction(
-      bool request_trusted_bidding_signals = true) {
+  void RunStandardAuction(bool request_trusted_bidding_signals = true) {
     StartStandardAuction(request_trusted_bidding_signals);
     auction_run_loop_->Run();
-    return result_;
   }
 
   // Starts the standard auction with the mock worklet service, and waits for
   // the service to receive the worklet construction calls.
-  void StartStandardAuctionWithMockService() {
+  //
+  // `num_expected_bidder_worklets` is the number of bidder worklets that are
+  // expected to be created.
+  void StartStandardAuctionWithMockService(
+      int num_expected_bidder_worklets = 2) {
     UseMockWorkletService();
     StartStandardAuction();
     mock_auction_process_manager_->WaitForWorklets(
-        /*num_bidders=*/2, /*num_sellers=*/1 + component_auctions_.size());
+        /*num_bidders=*/num_expected_bidder_worklets,
+        /*num_sellers=*/1 + component_auctions_.size());
   }
 
   // Runs an auction that exercises the extended private aggregation buyers
@@ -2031,6 +2034,7 @@
   const url::Origin kBidder1 = url::Origin::Create(kBidder1Url);
   const InterestGroupKey kBidder1Key{kBidder1, kBidder1Name};
   const GURL kBidder1TrustedSignalsUrl{"https://adplatform.com/signals1"};
+  const base::TimeDelta kBidder1CumulativeTimeout = base::Milliseconds(12345);
 
   const GURL kBidder2Url{"https://anotheradthing.com/bids.js"};
   const url::Origin kBidder2 = url::Origin::Create(kBidder2Url);
@@ -2038,6 +2042,13 @@
   const InterestGroupKey kBidder2Key{kBidder2, kBidder2Name};
   const GURL kBidder2TrustedSignalsUrl{"https://anotheradthing.com/signals2"};
 
+  const base::TimeDelta kAllBuyersCumulativeTimeout = base::Milliseconds(23456);
+
+  // Timeout tests can wait until this amount before a timeout, make sure
+  // nothing has happened, and then wait this amount, and check the timeout
+  // happened.
+  const base::TimeDelta kTinyTime = base::Milliseconds(1);
+
   absl::optional<std::vector<url::Origin>> interest_group_buyers_ = {
       {kBidder1, kBidder2}};
 
@@ -2465,20 +2476,20 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
-  EXPECT_FALSE(res.manually_aborted);
+  RunStandardAuction();
+  EXPECT_FALSE(result_.manually_aborted);
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad2.com-component1.com")},
-            res.ad_component_urls);
+            result_.ad_component_urls);
   EXPECT_THAT(
-      res.report_urls,
+      result_.report_urls,
       testing::UnorderedElementsAre(
           GURL("https://reporting.example.com/?highestScoringOtherBid=1&bid=2"),
           ReportWinUrl(/*bid=*/2, /*highest_scoring_other_bid=*/1,
                        /*made_highest_scoring_other_bid=*/false)));
   EXPECT_THAT(
-      res.ad_beacon_map,
+      result_.ad_beacon_map,
       testing::UnorderedElementsAre(
           testing::Pair(ReportingDestination::kSeller,
                         testing::ElementsAre(testing::Pair(
@@ -2507,7 +2518,7 @@
                             kExpectedReportResultPrivateAggregationRequest))));
 
   EXPECT_THAT(
-      res.private_aggregation_event_map,
+      result_.private_aggregation_event_map,
       testing::UnorderedElementsAre(testing::Pair(
           "click",
           ElementsAreRequests(
@@ -2518,7 +2529,7 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_TRUE(res.errors.empty());
+  EXPECT_TRUE(result_.errors.empty());
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -3889,12 +3900,12 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder1Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad1.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad1.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls,
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls,
               testing::UnorderedElementsAre(
                   GURL("https://reporting.example.com/1"),
                   GURL("https://buyer-reporting.example.com/1")));
@@ -3934,7 +3945,7 @@
   EXPECT_EQ(R"({"render_url":"https://ad1.com/","metadata":{"ads": true}})",
             result_.winning_group_ad_metadata);
   EXPECT_THAT(
-      res.errors,
+      result_.errors,
       testing::ElementsAre("Failed to load https://anotheradthing.com/bids.js "
                            "HTTP status = 404 Not Found."));
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
@@ -4048,12 +4059,12 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder1Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad1.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad1.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls,
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls,
               testing::UnorderedElementsAre(
                   GURL("https://reporting.example.com/1"),
                   GURL("https://buyer-reporting.example.com/1")));
@@ -4092,7 +4103,7 @@
               testing::UnorderedElementsAre(kBidder1Key));
   EXPECT_EQ(R"({"render_url":"https://ad1.com/","metadata":{"ads": true}})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors,
+  EXPECT_THAT(result_.errors,
               testing::ElementsAre("https://anotheradthing.com/bids.js "
                                    "`generateBid` is not a function."));
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
@@ -4119,15 +4130,16 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
-  EXPECT_FALSE(res.winning_group_id);
-  EXPECT_FALSE(res.ad_url);
-  EXPECT_TRUE(res.ad_component_urls.empty());
+  RunStandardAuction();
+  EXPECT_FALSE(result_.winning_group_id);
+  EXPECT_FALSE(result_.ad_url);
+  EXPECT_TRUE(result_.ad_component_urls.empty());
   EXPECT_TRUE(
       private_aggregation_manager_.TakePrivateAggregationRequests().empty());
-  EXPECT_TRUE(res.private_aggregation_event_map.empty());
-  EXPECT_THAT(res.interest_groups_that_bid, testing::UnorderedElementsAre());
-  EXPECT_THAT(res.errors,
+  EXPECT_TRUE(result_.private_aggregation_event_map.empty());
+  EXPECT_THAT(result_.interest_groups_that_bid,
+              testing::UnorderedElementsAre());
+  EXPECT_THAT(result_.errors,
               testing::UnorderedElementsAre(
                   "Failed to load https://adplatform.com/offers.js "
                   "HTTP status = 404 Not Found.",
@@ -4160,16 +4172,17 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
-  EXPECT_FALSE(res.winning_group_id);
-  EXPECT_FALSE(res.ad_url);
-  EXPECT_TRUE(res.ad_component_urls.empty());
+  RunStandardAuction();
+  EXPECT_FALSE(result_.winning_group_id);
+  EXPECT_FALSE(result_.ad_url);
+  EXPECT_TRUE(result_.ad_component_urls.empty());
   EXPECT_TRUE(
       private_aggregation_manager_.TakePrivateAggregationRequests().empty());
-  EXPECT_TRUE(res.private_aggregation_event_map.empty());
-  EXPECT_THAT(res.interest_groups_that_bid, testing::UnorderedElementsAre());
+  EXPECT_TRUE(result_.private_aggregation_event_map.empty());
+  EXPECT_THAT(result_.interest_groups_that_bid,
+              testing::UnorderedElementsAre());
   EXPECT_THAT(
-      res.errors,
+      result_.errors,
       testing::UnorderedElementsAre(
           "https://adplatform.com/offers.js `generateBid` is not a function.",
           "https://anotheradthing.com/bids.js `generateBid` is not a "
@@ -4209,10 +4222,10 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
-  EXPECT_FALSE(res.winning_group_id);
-  EXPECT_FALSE(res.ad_url);
-  EXPECT_TRUE(res.ad_component_urls.empty());
+  RunStandardAuction();
+  EXPECT_FALSE(result_.winning_group_id);
+  EXPECT_FALSE(result_.ad_url);
+  EXPECT_TRUE(result_.ad_component_urls.empty());
   EXPECT_THAT(
       private_aggregation_manager_.TakePrivateAggregationRequests(),
       testing::UnorderedElementsAre(
@@ -4225,11 +4238,11 @@
   EXPECT_TRUE(result_.private_aggregation_event_map.empty());
   EXPECT_THAT(result_.interest_groups_that_bid,
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
-  EXPECT_THAT(res.errors, testing::UnorderedElementsAre(
-                              "https://adstuff.publisher1.com/auction.js "
-                              "`scoreAd` is not a function.",
-                              "https://adstuff.publisher1.com/auction.js "
-                              "`scoreAd` is not a function."));
+  EXPECT_THAT(result_.errors, testing::UnorderedElementsAre(
+                                  "https://adstuff.publisher1.com/auction.js "
+                                  "`scoreAd` is not a function.",
+                                  "https://adstuff.publisher1.com/auction.js "
+                                  "`scoreAd` is not a function."));
   CheckHistograms(InterestGroupAuction::AuctionResult::kAllBidsRejected,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4262,12 +4275,12 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder1Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad1.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad1.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls,
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls,
               testing::UnorderedElementsAre(
                   GURL("https://reporting.example.com/1"),
                   GURL("https://buyer-reporting.example.com/1")));
@@ -4310,7 +4323,7 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad1.com/","metadata":{"ads": true}})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::ElementsAre());
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4321,17 +4334,18 @@
   // Tests to make sure that if seller script fails the other fetches are
   // cancelled, too.
   url_loader_factory_.AddResponse(kSellerUrl.spec(), "", net::HTTP_NOT_FOUND);
-  const Result& res = RunStandardAuction();
-  EXPECT_FALSE(res.winning_group_id);
-  EXPECT_FALSE(res.ad_url);
-  EXPECT_TRUE(res.ad_component_urls.empty());
+  RunStandardAuction();
+  EXPECT_FALSE(result_.winning_group_id);
+  EXPECT_FALSE(result_.ad_url);
+  EXPECT_TRUE(result_.ad_component_urls.empty());
   EXPECT_TRUE(
       private_aggregation_manager_.TakePrivateAggregationRequests().empty());
-  EXPECT_TRUE(res.private_aggregation_event_map.empty());
+  EXPECT_TRUE(result_.private_aggregation_event_map.empty());
 
   EXPECT_EQ(0, url_loader_factory_.NumPending());
-  EXPECT_THAT(res.interest_groups_that_bid, testing::UnorderedElementsAre());
-  EXPECT_THAT(res.errors,
+  EXPECT_THAT(result_.interest_groups_that_bid,
+              testing::UnorderedElementsAre());
+  EXPECT_THAT(result_.errors,
               testing::ElementsAre(
                   "Failed to load https://adstuff.publisher1.com/auction.js "
                   "HTTP status = 404 Not Found."));
@@ -4363,12 +4377,12 @@
                                          absl::nullopt, {"l1", "l2"},
                                          GURL("https://ad2.com")));
 
-  const Result& res = RunAuctionAndWait(kSellerUrl, std::move(bidders));
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
 
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_TRUE(result_.ad_component_urls.empty());
-  EXPECT_THAT(res.report_urls,
+  EXPECT_THAT(result_.report_urls,
               testing::UnorderedElementsAre(
                   GURL("https://reporting.example.com/2"),
                   GURL("https://buyer-reporting.example.com/2")));
@@ -4411,7 +4425,7 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::ElementsAre());
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4440,12 +4454,12 @@
   auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
                                          MakeAuctionScript());
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad2.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls,
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls,
               testing::UnorderedElementsAre(
                   GURL("https://reporting.example.com/2"),
                   GURL("https://buyer-reporting.example.com/2")));
@@ -4488,17 +4502,17 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::UnorderedElementsAre(
-                              "Failed to load "
-                              "https://adplatform.com/"
-                              "signals1?hostname=publisher1.com&keys=k1,k2&"
-                              "interestGroupNames=Ad+Platform "
-                              "HTTP status = 404 Not Found.",
-                              "Failed to load "
-                              "https://anotheradthing.com/"
-                              "signals2?hostname=publisher1.com&keys=l1,l2"
-                              "&interestGroupNames=Another+Ad+Thing "
-                              "HTTP status = 404 Not Found."));
+  EXPECT_THAT(result_.errors, testing::UnorderedElementsAre(
+                                  "Failed to load "
+                                  "https://adplatform.com/"
+                                  "signals1?hostname=publisher1.com&keys=k1,k2&"
+                                  "interestGroupNames=Ad+Platform "
+                                  "HTTP status = 404 Not Found.",
+                                  "Failed to load "
+                                  "https://anotheradthing.com/"
+                                  "signals2?hostname=publisher1.com&keys=l1,l2"
+                                  "&interestGroupNames=Another+Ad+Thing "
+                                  "HTTP status = 404 Not Found."));
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4531,13 +4545,14 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad2.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls, testing::UnorderedElementsAre(GURL(
-                                   "https://buyer-reporting.example.com/2")));
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls,
+              testing::UnorderedElementsAre(
+                  GURL("https://buyer-reporting.example.com/2")));
   EXPECT_THAT(
       result_.ad_beacon_map,
       testing::UnorderedElementsAre(
@@ -4575,7 +4590,7 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::ElementsAre());
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4610,13 +4625,14 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad2.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls, testing::UnorderedElementsAre(
-                                   GURL("https://reporting.example.com/2")));
+            result_.ad_component_urls);
+  EXPECT_THAT(
+      result_.report_urls,
+      testing::UnorderedElementsAre(GURL("https://reporting.example.com/2")));
   EXPECT_THAT(
       result_.ad_beacon_map,
       testing::UnorderedElementsAre(
@@ -4650,7 +4666,7 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::ElementsAre());
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4685,13 +4701,13 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad2.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls, testing::UnorderedElementsAre());
-  EXPECT_THAT(res.ad_beacon_map,
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls, testing::UnorderedElementsAre());
+  EXPECT_THAT(result_.ad_beacon_map,
               testing::UnorderedElementsAreArray(kEmptyAdBeaconMap));
   EXPECT_THAT(
       private_aggregation_manager_.TakePrivateAggregationRequests(),
@@ -4717,7 +4733,7 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::ElementsAre());
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -4759,14 +4775,15 @@
            "&interestGroupNames=Another+Ad+Thing"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_EQ(kBidder2Key, result_.winning_group_id);
-  EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
+  EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
   EXPECT_EQ(std::vector<GURL>{GURL("https://ad2.com-component1.com")},
-            res.ad_component_urls);
-  EXPECT_THAT(res.report_urls, testing::UnorderedElementsAre(GURL(
-                                   "https://seller.signals.were.null.test/")));
-  EXPECT_THAT(res.ad_beacon_map,
+            result_.ad_component_urls);
+  EXPECT_THAT(result_.report_urls,
+              testing::UnorderedElementsAre(
+                  GURL("https://seller.signals.were.null.test/")));
+  EXPECT_THAT(result_.ad_beacon_map,
               testing::UnorderedElementsAreArray(kEmptyAdBeaconMap));
   EXPECT_THAT(
       private_aggregation_manager_.TakePrivateAggregationRequests(),
@@ -4790,9 +4807,9 @@
               testing::UnorderedElementsAre(kBidder1Key, kBidder2Key));
   EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
             result_.winning_group_ad_metadata);
-  EXPECT_THAT(res.errors, testing::ElementsAre(base::StringPrintf(
-                              "%s `reportResult` is not a function.",
-                              kSellerUrl.spec().c_str())));
+  EXPECT_THAT(result_.errors, testing::ElementsAre(base::StringPrintf(
+                                  "%s `reportResult` is not a function.",
+                                  kSellerUrl.spec().c_str())));
   CheckHistograms(InterestGroupAuction::AuctionResult::kSuccess,
                   /*expected_interest_groups=*/2, /*expected_owners=*/2,
                   /*expected_sellers=*/1);
@@ -8414,6 +8431,280 @@
               testing::ElementsAre(GURL("https://adplatform.com/metrics/5")));
 }
 
+// Test the case where the only bidder times out due to the
+// perBuyerCumulativeTimeouts.
+TEST_F(AuctionRunnerTest, PerBuyerCumulativeTimeouts) {
+  interest_group_buyers_ = {{kBidder1}};
+  StartStandardAuctionWithMockService(/*num_expected_bidder_worklets=*/1);
+
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  ASSERT_TRUE(bidder1_worklet);
+
+  task_environment()->FastForwardBy(kBidder1CumulativeTimeout - kTinyTime);
+  EXPECT_FALSE(auction_complete_);
+  task_environment()->FastForwardBy(kTinyTime);
+  EXPECT_TRUE(auction_complete_);
+  auction_run_loop_->Run();
+  EXPECT_THAT(result_.errors,
+              testing::UnorderedElementsAre(
+                  "https://adplatform.com/offers.js perBuyerCumulativeTimeout "
+                  "exceeded during bid generation."));
+  EXPECT_EQ(absl::nullopt, result_.winning_group_id);
+}
+
+// Test the case where the perBuyerCumulativeTimeout expires during the
+// scoreAd() call. The bid should not be timed out.
+TEST_F(AuctionRunnerTest,
+       PerBuyerCumulativeTimeoutsTimeoutPassesDuringScoreAd) {
+  interest_group_buyers_ = {{kBidder1}};
+  StartStandardAuctionWithMockService(/*num_expected_bidder_worklets=*/1);
+
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  ASSERT_TRUE(bidder1_worklet);
+
+  // The timeout isn't quite hit.
+  task_environment()->FastForwardBy(kBidder1CumulativeTimeout - kTinyTime);
+  EXPECT_FALSE(auction_complete_);
+
+  // Bid generation completes.
+  bidder1_worklet->InvokeGenerateBidCallback(/*bid=*/2,
+                                             GURL("https://ad1.com/"));
+
+  // More than the timeout time passes, but since the bid is being blocked on
+  // the seller, there should be no timeout.
+  task_environment()->FastForwardBy(2 * kBidder1CumulativeTimeout);
+  EXPECT_FALSE(auction_complete_);
+
+  // Score the ad.
+  auto score_ad_params = seller_worklet->WaitForScoreAd();
+  EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
+  EXPECT_EQ(2, score_ad_params.bid);
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          /*reject_reason=*/
+          auction_worklet::mojom::RejectReason::kNotAvailable,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*scoring_signals_data_version=*/0,
+          /*has_scoring_signals_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
+
+  // Finish the auction.
+  seller_worklet->WaitForReportResult();
+  seller_worklet->InvokeReportResultCallback();
+  mock_auction_process_manager_->WaitForWinningBidderReload();
+  bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  bidder1_worklet->WaitForReportWin();
+  bidder1_worklet->InvokeReportWinCallback();
+  auction_run_loop_->Run();
+
+  EXPECT_THAT(result_.errors, testing::UnorderedElementsAre());
+  EXPECT_EQ(kBidder1Key, result_.winning_group_id);
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
+}
+
+// Test the case where a pending promise delays the start of the
+// perBuyerCumulativeTimeout, but generating a bid still times out since
+// perBuyerCumulativeTimeout passes after promise resolution.
+TEST_F(AuctionRunnerTest,
+       PerBuyerCumulativeTimeoutsPromiseDelaysTimeoutButStillTimesOut) {
+  use_promise_for_buyer_cumulative_timeouts_ = true;
+  interest_group_buyers_ = {{kBidder1}};
+  StartStandardAuctionWithMockService(/*num_expected_bidder_worklets=*/1);
+
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  ASSERT_TRUE(bidder1_worklet);
+
+  // The timeout duration passes, but since the seller is being waited on, too,
+  // this doesn't count towards the timeout.
+  task_environment()->FastForwardBy(2 * kBidder1CumulativeTimeout);
+  EXPECT_FALSE(auction_complete_);
+
+  // Feed in perBuyerCumulativeTimeouts.
+  abortable_ad_auction_->ResolvedBuyerTimeoutsPromise(
+      blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0),
+      blink::mojom::AuctionAdConfigBuyerTimeoutField::
+          kPerBuyerCumulativeTimeouts,
+      MakeBuyerCumulativeTimeouts(/*use_promise=*/false).value());
+
+  // The timeout passes again, but this time, it counts towards the cumulative
+  // timeout.
+  task_environment()->FastForwardBy(kBidder1CumulativeTimeout - kTinyTime);
+  EXPECT_FALSE(auction_complete_);
+  task_environment()->FastForwardBy(kTinyTime);
+  EXPECT_TRUE(auction_complete_);
+
+  auction_run_loop_->Run();
+  EXPECT_THAT(result_.errors,
+              testing::UnorderedElementsAre(
+                  "https://adplatform.com/offers.js perBuyerCumulativeTimeout "
+                  "exceeded during bid generation."));
+  EXPECT_EQ(absl::nullopt, result_.winning_group_id);
+}
+
+// Test the case where a pending promise delays the start of the
+// perBuyerCumulativeTimeout, and a bid is ultimately generated successfully
+// because of the delayed promise resolution.
+TEST_F(AuctionRunnerTest,
+       PerBuyerCumulativeTimeoutsPromiseDelaysTimeoutAndNoTimeout) {
+  use_promise_for_buyer_cumulative_timeouts_ = true;
+  interest_group_buyers_ = {{kBidder1}};
+  StartStandardAuctionWithMockService(/*num_expected_bidder_worklets=*/1);
+
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  ASSERT_TRUE(bidder1_worklet);
+
+  // The timeout duration passes, but since the seller is being waited on, too,
+  // this doesn't count towards the timeout.
+  task_environment()->FastForwardBy(2 * kBidder1CumulativeTimeout);
+  EXPECT_FALSE(auction_complete_);
+
+  // Feed in perBuyerCumulativeTimeouts.
+  abortable_ad_auction_->ResolvedBuyerTimeoutsPromise(
+      blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0),
+      blink::mojom::AuctionAdConfigBuyerTimeoutField::
+          kPerBuyerCumulativeTimeouts,
+      MakeBuyerCumulativeTimeouts(/*use_promise=*/false).value());
+
+  // The timeout doesn't quite pass after the promise is resolved.
+  task_environment()->FastForwardBy(kBidder1CumulativeTimeout - kTinyTime);
+  EXPECT_FALSE(auction_complete_);
+
+  // Bid generation completes.
+  bidder1_worklet->InvokeGenerateBidCallback(/*bid=*/2,
+                                             GURL("https://ad1.com/"));
+
+  // Score the ad.
+  auto score_ad_params = seller_worklet->WaitForScoreAd();
+  EXPECT_EQ(kBidder1, score_ad_params.interest_group_owner);
+  EXPECT_EQ(2, score_ad_params.bid);
+  mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
+      std::move(score_ad_params.score_ad_client))
+      ->OnScoreAdComplete(
+          /*score=*/10,
+          /*reject_reason=*/
+          auction_worklet::mojom::RejectReason::kNotAvailable,
+          auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
+          /*scoring_signals_data_version=*/0,
+          /*has_scoring_signals_data_version=*/false,
+          /*debug_loss_report_url=*/absl::nullopt,
+          /*debug_win_report_url=*/absl::nullopt, /*pa_requests=*/{},
+          /*errors=*/{});
+
+  // Finish the auction.
+  seller_worklet->WaitForReportResult();
+  seller_worklet->InvokeReportResultCallback();
+  mock_auction_process_manager_->WaitForWinningBidderReload();
+  bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  bidder1_worklet->WaitForReportWin();
+  bidder1_worklet->InvokeReportWinCallback();
+  auction_run_loop_->Run();
+
+  EXPECT_THAT(result_.errors, testing::UnorderedElementsAre());
+  EXPECT_EQ(kBidder1Key, result_.winning_group_id);
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
+}
+
+// Test that the cumulative timeout only starts once a process is assigned.
+TEST_F(AuctionRunnerTest, PerBuyerCumulativeTimeoutsWaitForProcess) {
+  // Create AuctionProcessManager in advance of starting the auction so can
+  // create worklets before the auction starts.
+  UseMockWorkletService();
+
+  // Fill up all bidding process slots.
+  std::vector<std::unique_ptr<AuctionProcessManager::ProcessHandle>>
+      busy_processes;
+  for (size_t i = 0; i < AuctionProcessManager::kMaxBidderProcesses; ++i) {
+    busy_processes.push_back(
+        std::make_unique<AuctionProcessManager::ProcessHandle>());
+    url::Origin origin = url::Origin::Create(
+        GURL(base::StringPrintf("https://blocking.bidder.%zu.test", i)));
+    EXPECT_TRUE(auction_process_manager_->RequestWorkletService(
+        AuctionProcessManager::WorkletType::kBidder, origin,
+        scoped_refptr<SiteInstance>(), &*busy_processes.back(),
+        base::BindOnce(
+            []() { ADD_FAILURE() << "This should not be called"; })));
+  }
+  task_environment()->RunUntilIdle();
+
+  // Start a 1-bidder auction.
+  interest_group_buyers_ = {{kBidder1}};
+  StartStandardAuction();
+
+  // The timeout should not have started, since the bidder is still waiting on a
+  // process slot.
+  task_environment()->FastForwardBy(2 * kBidder1CumulativeTimeout);
+  EXPECT_FALSE(auction_complete_);
+
+  // Free up a process slot.
+  busy_processes.erase(busy_processes.begin());
+
+  // Wait for all worklet requests.
+  mock_auction_process_manager_->WaitForWorklets(/*num_bidders=*/1,
+                                                 /*num_sellers=*/1);
+
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  ASSERT_TRUE(bidder1_worklet);
+
+  // Wait for the timeout to pass again. This time, it should result in the
+  // bidder timing out.
+  task_environment()->FastForwardBy(kBidder1CumulativeTimeout - kTinyTime);
+  EXPECT_FALSE(auction_complete_);
+  task_environment()->FastForwardBy(kTinyTime);
+  EXPECT_TRUE(auction_complete_);
+
+  auction_run_loop_->Run();
+  EXPECT_THAT(result_.errors,
+              testing::UnorderedElementsAre(
+                  "https://adplatform.com/offers.js perBuyerCumulativeTimeout "
+                  "exceeded during bid generation."));
+  EXPECT_EQ(absl::nullopt, result_.winning_group_id);
+}
+
+// Test the case where the only bidder times out due to the
+// perBuyerCumulativeTimeout's "*" field.
+TEST_F(AuctionRunnerTest, PerBuyerCumulativeTimeoutsAllBuyersTimeout) {
+  interest_group_buyers_ = {{kBidder2}};
+  StartStandardAuctionWithMockService(/*num_expected_bidder_worklets=*/1);
+
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder2_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
+  ASSERT_TRUE(bidder2_worklet);
+
+  task_environment()->FastForwardBy(kAllBuyersCumulativeTimeout - kTinyTime);
+  EXPECT_FALSE(auction_complete_);
+  task_environment()->FastForwardBy(kTinyTime);
+  EXPECT_TRUE(auction_complete_);
+  auction_run_loop_->Run();
+  EXPECT_THAT(result_.errors,
+              testing::UnorderedElementsAre(
+                  "https://anotheradthing.com/bids.js "
+                  "perBuyerCumulativeTimeout exceeded during bid generation."));
+  EXPECT_EQ(absl::nullopt, result_.winning_group_id);
+}
+
 // Auction with only one interest group participating. The priority calculated
 // using its priority vector is negative, so it should be filtered out, and
 // there should be no winner.
@@ -9340,10 +9631,10 @@
   // Want AuctionRunner still around to make sure that it handles Abort() OK
   // in that timing.
   dont_reset_auction_runner_ = true;
-  const Result& result = RunAuctionAndWait(kSellerUrl, std::move(bidders));
-  EXPECT_EQ(kBidder1Name, result.winning_group_id->name);
-  EXPECT_FALSE(result.manually_aborted);
-  EXPECT_THAT(result.errors, testing::ElementsAre());
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+  EXPECT_EQ(kBidder1Name, result_.winning_group_id->name);
+  EXPECT_FALSE(result_.manually_aborted);
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
   abortable_ad_auction_->Abort();
   task_environment()->RunUntilIdle();
   auction_runner_.reset();
@@ -10122,9 +10413,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10158,9 +10448,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders,
@@ -10197,8 +10486,7 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetAllSellerCapabilities(
-              blink::InterestGroup::SellerCapabilities::kLatencyStats)
+          .SetAllSellerCapabilities(blink::SellerCapabilities::kLatencyStats)
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10235,9 +10523,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10274,9 +10561,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10310,9 +10596,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10346,9 +10631,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10383,9 +10667,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10443,9 +10726,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10470,9 +10752,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10503,9 +10784,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(bidders, {kTrustedSignalsFetchDuration});
@@ -10538,9 +10818,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
   bidders.emplace_back(MakeInterestGroup(
       blink::TestInterestGroupBuilder(kBidder2, kBidder2Name)
@@ -10549,9 +10828,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(
@@ -10599,9 +10877,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
   bidders.emplace_back(MakeInterestGroup(
       blink::TestInterestGroupBuilder(kBidder2, kBidder2Name)
@@ -10610,9 +10887,8 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities(
-              {{{url::Origin::Create(kSellerUrl),
-                 blink::InterestGroup::SellerCapabilities::kLatencyStats}}})
+          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
+                                    blink::SellerCapabilities::kLatencyStats}}})
           .Build()));
 
   RunExtendedPABuyersAuction(
@@ -10650,9 +10926,9 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
-                                    blink::InterestGroup::SellerCapabilities::
-                                        kInterestGroupCounts}}})
+          .SetSellerCapabilities(
+              {{{url::Origin::Create(kSellerUrl),
+                 blink::SellerCapabilities::kInterestGroupCounts}}})
           .Build()));
   bidders.emplace_back(MakeInterestGroup(
       blink::TestInterestGroupBuilder(kBidder2, kBidder2Name)
@@ -10661,9 +10937,9 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
-                                    blink::InterestGroup::SellerCapabilities::
-                                        kInterestGroupCounts}}})
+          .SetSellerCapabilities(
+              {{{url::Origin::Create(kSellerUrl),
+                 blink::SellerCapabilities::kInterestGroupCounts}}})
           .Build()));
 
   RunExtendedPABuyersAuction(
@@ -10706,9 +10982,9 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
-                                    blink::InterestGroup::SellerCapabilities::
-                                        kInterestGroupCounts}}})
+          .SetSellerCapabilities(
+              {{{url::Origin::Create(kSellerUrl),
+                 blink::SellerCapabilities::kInterestGroupCounts}}})
           .Build()));
   bidders.emplace_back(MakeInterestGroup(
       blink::TestInterestGroupBuilder(kBidder2, kBidder2Name)
@@ -10717,9 +10993,9 @@
           .SetTrustedBiddingSignalsKeys({{"k1", "k2"}})
           .SetAds({{blink::InterestGroup::Ad(GURL("https://ad1.com"),
                                              absl::nullopt)}})
-          .SetSellerCapabilities({{{url::Origin::Create(kSellerUrl),
-                                    blink::InterestGroup::SellerCapabilities::
-                                        kInterestGroupCounts}}})
+          .SetSellerCapabilities(
+              {{{url::Origin::Create(kSellerUrl),
+                 blink::SellerCapabilities::kInterestGroupCounts}}})
           .Build()));
 
   RunExtendedPABuyersAuction(
@@ -10933,17 +11209,17 @@
         /*trusted_bidding_signals_url=*/absl::nullopt,
         /*trusted_bidding_signals_keys=*/{}, GURL("https://ad3.com")));
 
-    const Result& res = RunAuctionAndWait(kSellerUrl, std::move(bidders));
+    RunAuctionAndWait(kSellerUrl, std::move(bidders));
 
-    EXPECT_EQ(4u, res.debug_loss_report_urls.size());
-    EXPECT_EQ(2u, res.debug_win_report_urls.size());
-    EXPECT_EQ(2u, res.report_urls.size());
+    EXPECT_EQ(4u, result_.debug_loss_report_urls.size());
+    EXPECT_EQ(2u, result_.debug_win_report_urls.size());
+    EXPECT_EQ(2u, result_.report_urls.size());
 
     // Winner has ad2 or ad3.
-    if (res.ad_url == "https://ad2.com/") {
+    if (result_.ad_url == "https://ad2.com/") {
       seen_ad2_win = true;
       EXPECT_THAT(
-          res.debug_loss_report_urls,
+          result_.debug_loss_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugLossReportBaseUrl,
@@ -10975,7 +11251,7 @@
                   /*bid=*/4)));
 
       EXPECT_THAT(
-          res.debug_win_report_urls,
+          result_.debug_win_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugWinReportBaseUrl,
@@ -10992,16 +11268,16 @@
                                      /*made_highest_scoring_other_bid=*/false),
                   /*bid=*/3)));
 
-      EXPECT_THAT(res.report_urls,
+      EXPECT_THAT(result_.report_urls,
                   testing::UnorderedElementsAre(
                       GURL("https://reporting.example.com/"
                            "?highestScoringOtherBid=4&bid=3"),
                       ReportWinUrl(/*bid=*/3, /*highest_scoring_other_bid=*/4,
                                    /*made_highest_scoring_other_bid=*/false)));
-    } else if (res.ad_url == "https://ad3.com/") {
+    } else if (result_.ad_url == "https://ad3.com/") {
       seen_ad3_win = true;
       EXPECT_THAT(
-          res.debug_loss_report_urls,
+          result_.debug_loss_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugLossReportBaseUrl,
@@ -11033,7 +11309,7 @@
                   /*bid=*/3)));
 
       EXPECT_THAT(
-          res.debug_win_report_urls,
+          result_.debug_win_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugWinReportBaseUrl,
@@ -11051,7 +11327,7 @@
                                      /*made_highest_scoring_other_bid=*/false),
                   /*bid=*/4)));
 
-      EXPECT_THAT(res.report_urls,
+      EXPECT_THAT(result_.report_urls,
                   testing::UnorderedElementsAre(
                       GURL("https://reporting.example.com/"
                            "?highestScoringOtherBid=3&bid=4"),
@@ -11104,30 +11380,30 @@
         /*trusted_bidding_signals_url=*/absl::nullopt,
         /*trusted_bidding_signals_keys=*/{}, GURL("https://ad3.com")));
 
-    const Result& res = RunAuctionAndWait(kSellerUrl, std::move(bidders));
+    RunAuctionAndWait(kSellerUrl, std::move(bidders));
 
     double highest_scoring_other_bid = 0.0;
     if (base::Contains(
-            res.report_urls,
+            result_.report_urls,
             "https://reporting.example.com/?highestScoringOtherBid=1&bid=3",
             &GURL::spec)) {
       highest_scoring_other_bid = 1;
-    } else if (base::Contains(res.report_urls,
+    } else if (base::Contains(result_.report_urls,
                               "https://reporting.example.com/"
                               "?highestScoringOtherBid=2&bid=3",
                               &GURL::spec)) {
       highest_scoring_other_bid = 2;
     }
 
-    EXPECT_EQ(GURL("https://ad3.com/"), res.ad_url);
-    EXPECT_EQ(4u, res.debug_loss_report_urls.size());
-    EXPECT_EQ(2u, res.debug_win_report_urls.size());
-    EXPECT_EQ(2u, res.report_urls.size());
+    EXPECT_EQ(GURL("https://ad3.com/"), result_.ad_url);
+    EXPECT_EQ(4u, result_.debug_loss_report_urls.size());
+    EXPECT_EQ(2u, result_.debug_win_report_urls.size());
+    EXPECT_EQ(2u, result_.report_urls.size());
 
     if (highest_scoring_other_bid == 1) {
       seen_bid1 = true;
       EXPECT_THAT(
-          res.debug_loss_report_urls,
+          result_.debug_loss_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugLossReportBaseUrl,
@@ -11159,7 +11435,7 @@
                   /*bid=*/2)));
 
       EXPECT_THAT(
-          res.debug_win_report_urls,
+          result_.debug_win_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(kBidderDebugWinReportBaseUrl,
                              PostAuctionSignals(
@@ -11176,7 +11452,7 @@
                                  /*made_highest_scoring_other_bid=*/true),
                              /*bid=*/3)));
 
-      EXPECT_THAT(res.report_urls,
+      EXPECT_THAT(result_.report_urls,
                   testing::UnorderedElementsAre(
                       GURL("https://reporting.example.com/"
                            "?highestScoringOtherBid=1&bid=3"),
@@ -11185,7 +11461,7 @@
     } else if (highest_scoring_other_bid == 2) {
       seen_bid2 = true;
       EXPECT_THAT(
-          res.debug_loss_report_urls,
+          result_.debug_loss_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugLossReportBaseUrl,
@@ -11217,7 +11493,7 @@
                   /*bid=*/2)));
 
       EXPECT_THAT(
-          res.debug_win_report_urls,
+          result_.debug_win_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(kBidderDebugWinReportBaseUrl,
                              PostAuctionSignals(
@@ -11234,7 +11510,7 @@
                                  /*made_highest_scoring_other_bid=*/true),
                              /*bid=*/3)));
 
-      EXPECT_THAT(res.report_urls,
+      EXPECT_THAT(result_.report_urls,
                   testing::UnorderedElementsAre(
                       GURL("https://reporting.example.com/"
                            "?highestScoringOtherBid=2&bid=3"),
@@ -11278,19 +11554,19 @@
         /*trusted_bidding_signals_url=*/absl::nullopt,
         /*trusted_bidding_signals_keys=*/{}, GURL("https://ad3.com")));
 
-    const Result& res = RunAuctionAndWait(kSellerUrl, std::move(bidders));
+    RunAuctionAndWait(kSellerUrl, std::move(bidders));
 
-    EXPECT_EQ(GURL("https://ad3.com/"), res.ad_url);
-    EXPECT_EQ(4u, res.debug_loss_report_urls.size());
-    EXPECT_EQ(2u, res.debug_win_report_urls.size());
-    EXPECT_EQ(2u, res.report_urls.size());
+    EXPECT_EQ(GURL("https://ad3.com/"), result_.ad_url);
+    EXPECT_EQ(4u, result_.debug_loss_report_urls.size());
+    EXPECT_EQ(2u, result_.debug_win_report_urls.size());
+    EXPECT_EQ(2u, result_.report_urls.size());
     double highest_scoring_other_bid = 0.0;
     if (base::Contains(
-            res.report_urls,
+            result_.report_urls,
             "https://reporting.example.com/?highestScoringOtherBid=1&bid=3",
             &GURL::spec)) {
       highest_scoring_other_bid = 1;
-    } else if (base::Contains(res.report_urls,
+    } else if (base::Contains(result_.report_urls,
                               "https://reporting.example.com/"
                               "?highestScoringOtherBid=2&bid=3",
                               &GURL::spec)) {
@@ -11300,7 +11576,7 @@
     if (highest_scoring_other_bid == 1) {
       seen_bid1 = true;
       EXPECT_THAT(
-          res.debug_loss_report_urls,
+          result_.debug_loss_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugLossReportBaseUrl,
@@ -11332,7 +11608,7 @@
                   /*bid=*/2)));
 
       EXPECT_THAT(
-          res.debug_win_report_urls,
+          result_.debug_win_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugWinReportBaseUrl,
@@ -11349,7 +11625,7 @@
                                      /*made_highest_scoring_other_bid=*/false),
                   /*bid=*/3)));
 
-      EXPECT_THAT(res.report_urls,
+      EXPECT_THAT(result_.report_urls,
                   testing::UnorderedElementsAre(
                       GURL("https://reporting.example.com/"
                            "?highestScoringOtherBid=1&bid=3"),
@@ -11358,7 +11634,7 @@
     } else if (highest_scoring_other_bid == 2) {
       seen_bid2 = true;
       EXPECT_THAT(
-          res.debug_loss_report_urls,
+          result_.debug_loss_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugLossReportBaseUrl,
@@ -11390,7 +11666,7 @@
                   /*bid=*/2)));
 
       EXPECT_THAT(
-          res.debug_win_report_urls,
+          result_.debug_win_report_urls,
           testing::UnorderedElementsAre(
               DebugReportUrl(
                   kBidderDebugWinReportBaseUrl,
@@ -11407,7 +11683,7 @@
                                      /*made_highest_scoring_other_bid=*/false),
                   /*bid=*/3)));
 
-      EXPECT_THAT(res.report_urls,
+      EXPECT_THAT(result_.report_urls,
                   testing::UnorderedElementsAre(
                       GURL("https://reporting.example.com/"
                            "?highestScoringOtherBid=2&bid=3"),
@@ -12500,10 +12776,10 @@
            "?hostname=publisher1.com&keys=l1,l2"),
       kBidder2SignalsJson);
 
-  const Result& res = RunStandardAuction();
+  RunStandardAuction();
   EXPECT_TRUE(
       private_aggregation_manager_.TakePrivateAggregationRequests().empty());
-  EXPECT_TRUE(res.private_aggregation_event_map.empty());
+  EXPECT_TRUE(result_.private_aggregation_event_map.empty());
 }
 
 class AuctionRunnerKAnonTest : public AuctionRunnerTest,
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index 1325c81f..5bbb5366 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <cmath>
 #include <iterator>
+#include <list>
 #include <map>
 #include <memory>
 #include <string>
@@ -64,7 +65,7 @@
 
 namespace {
 
-constexpr base::TimeDelta kMaxTimeout = base::Milliseconds(500);
+constexpr base::TimeDelta kMaxPerBuyerTimeout = base::Milliseconds(500);
 
 // For group freshness metrics.
 constexpr base::TimeDelta kGroupFreshnessMin = base::Minutes(1);
@@ -195,7 +196,7 @@
 // `interest_group`'s seller capabilities has authorized `capability` for
 // `seller`.
 bool CanReportPaBuyersValue(const blink::InterestGroup& interest_group,
-                            blink::InterestGroup::SellerCapabilities capability,
+                            blink::SellerCapabilities capability,
                             const url::Origin& seller) {
   if (interest_group.seller_capabilities) {
     auto it = interest_group.seller_capabilities->find(seller);
@@ -252,6 +253,47 @@
   return it->second;
 }
 
+// Retrieves the timeout from `buyer_timeouts` associated with `buyer`, if any.
+// Used for both `buyer_timeouts` and `buyer_cumulative_timeouts`, stored in
+// AuctionConfigs. Callers should use PerBuyerTimeout() and
+// PerBuyerCumulativeTimeout() instead, since those apply the timeout limit,
+// when applicable.
+absl::optional<base::TimeDelta> PerBuyerTimeoutHelper(
+    const url::Origin& buyer,
+    const blink::AuctionConfig::MaybePromiseBuyerTimeouts& buyer_timeouts) {
+  DCHECK(!buyer_timeouts.is_promise());
+  const auto& per_buyer_timeouts = buyer_timeouts.value().per_buyer_timeouts;
+  if (per_buyer_timeouts.has_value()) {
+    auto it = per_buyer_timeouts->find(buyer);
+    if (it != per_buyer_timeouts->end()) {
+      return it->second;
+    }
+  }
+  const auto& all_buyers_timeout = buyer_timeouts.value().all_buyers_timeout;
+  if (all_buyers_timeout.has_value()) {
+    return all_buyers_timeout.value();
+  }
+  return absl::nullopt;
+}
+
+absl::optional<base::TimeDelta> PerBuyerTimeout(
+    const url::Origin& buyer,
+    const blink::AuctionConfig& auction_config) {
+  absl::optional<base::TimeDelta> out = PerBuyerTimeoutHelper(
+      buyer, auction_config.non_shared_params.buyer_timeouts);
+  if (!out) {
+    return out;
+  }
+  return std::min(*out, kMaxPerBuyerTimeout);
+}
+
+absl::optional<base::TimeDelta> PerBuyerCumulativeTimeout(
+    const url::Origin& buyer,
+    const blink::AuctionConfig& auction_config) {
+  return PerBuyerTimeoutHelper(
+      buyer, auction_config.non_shared_params.buyer_cumulative_timeouts);
+}
+
 }  // namespace
 
 InterestGroupAuction::BidState::BidState() = default;
@@ -501,10 +543,9 @@
         std::move(pa_requests), errors);
   }
 
-  // Closes all Mojo pipes and release all weak pointers.
+  // Closes all Mojo pipes, releases all weak pointers, and stops the timeout
+  // timer.
   void ClosePipes() {
-    // This is needed in addition to closing worklet pipes since the callbacks
-    // passed to Mojo pipes this class doesn't own aren't cancellable.
     weak_ptr_factory_.InvalidateWeakPtrs();
 
     for (auto& bid_state : bid_states_) {
@@ -513,6 +554,11 @@
     // No need to clear `generate_bid_client_receiver_set_`, since
     // CloseBidStatePipes() should take care of that.
     DCHECK(generate_bid_client_receiver_set_.empty());
+
+    // Need to stop the timer - this is called on completion and on certain
+    // errors. Don't want the timer to trigger anything after there's been a
+    // failure already.
+    cumulative_buyer_timeout_timer_.Stop();
   }
 
   // Returns true if this buyer has any interest groups that will potentially
@@ -649,6 +695,18 @@
   }
 
   void NotifyConfigPromisesResolved() {
+    DCHECK(auction_->config_promises_resolved_);
+
+    // If there are no outstanding bids, just do nothing. It's safest to exit
+    // early in the case that bidder worklet process crashed or failed to fetch
+    // the necessary script(s) before all config promises were resolved, rather
+    // than rely on everything handling that case correctly.
+    if (num_outstanding_bids_ == 0) {
+      return;
+    }
+
+    MaybeStartCumulativeTimeoutTimer();
+
     for (const auto& bid_state : bid_states_) {
       FinishGenerateBidIfReady(bid_state.get());
     }
@@ -701,13 +759,21 @@
         AuctionWorkletManager::FatalErrorType::kWorkletCrash) {
       // Ignore default error message in case of crash. Instead, use a more
       // specific one.
-      auction_->errors_.push_back(
-          base::StrCat({bid_state->bidder->interest_group.bidding_url->spec(),
-                        " crashed while trying to run generateBid()."}));
+      OnFatalError(
+          bid_state,
+          {base::StrCat({bid_state->bidder->interest_group.bidding_url->spec(),
+                         " crashed while trying to run generateBid()."})});
     } else {
-      auction_->errors_.insert(auction_->errors_.end(), errors.begin(),
-                               errors.end());
+      OnFatalError(bid_state, errors);
     }
+  }
+
+  // Called in the case of a fatal error that prevents the `bid_state` worklet
+  // from bidding.
+  void OnFatalError(BidState* bid_state, std::vector<std::string> errors) {
+    auction_->errors_.insert(auction_->errors_.end(),
+                             std::make_move_iterator(errors.begin()),
+                             std::make_move_iterator(errors.end()));
 
     // If waiting on bidding signals, the bidder needs to be removed in the same
     // way as if it had a new negative priority value, so reuse that logic. The
@@ -771,6 +837,13 @@
   // Invoked whenever the AuctionWorkletManager has provided a BidderWorket
   // for the bidder identified by `bid_state`. Starts generating a bid.
   void OnBidderWorkletReceived(BidState* bid_state) {
+    if (!bidder_process_received_) {
+      // All bidder worklets are expected to be loaded in the same process, so
+      // as soon as any worklet has been received, can set this to true.
+      bidder_process_received_ = true;
+      MaybeStartCumulativeTimeoutTimer();
+    }
+
     const blink::InterestGroup& interest_group =
         bid_state->bidder->interest_group;
 
@@ -859,7 +932,7 @@
         auction_->config_->non_shared_params.auction_signals.value(),
         GetPerBuyerSignals(*auction_->config_,
                            bid_state->bidder->interest_group.owner),
-        auction_->PerBuyerTimeout(bid_state),
+        PerBuyerTimeout(owner_, *auction_->config_),
         GetDirectFromSellerPerBuyerSignals(
             url_builder, bid_state->bidder->interest_group.owner),
         GetDirectFromSellerAuctionSignals(url_builder));
@@ -1125,10 +1198,79 @@
     --num_outstanding_bids_;
     if (num_outstanding_bids_ == 0) {
       DCHECK_EQ(num_outstanding_bidding_signals_received_calls_, 0);
+      // Pipes should already be closed at this point, but the
+      // `cumulative_buyer_timeout_timer_` needs to be stopped if it's running,
+      // and it's safest to keep all logic to stop everything `this` may be
+      // doing in one place.
+      ClosePipes();
+
       auction_->OnBidSourceDone();
     }
   }
 
+  void MaybeStartCumulativeTimeoutTimer() {
+    // This should only be called when there are outstanding bids.
+    DCHECK_GT(num_outstanding_bids_, 0);
+
+    // Do nothing if still waiting on the seller to provide more of the
+    // AuctionConfig, or waiting on a process to be assigned (which would mean
+    // that this may be waiting behind other buyers).
+    if (!auction_->config_promises_resolved_ || !bidder_process_received_) {
+      return;
+    }
+
+    DCHECK(!cumulative_buyer_timeout_timer_.IsRunning());
+
+    // Get cumulative buyer timeout. Note that this must be done after the
+    // `config_promises_resolved_` check above.
+    absl::optional<base::TimeDelta> cumulative_buyer_timeout =
+        PerBuyerCumulativeTimeout(owner_, *auction_->config_);
+
+    // Nothing to do if there's no cumulative timeout.
+    if (!cumulative_buyer_timeout) {
+      return;
+    }
+
+    cumulative_buyer_timeout_timer_.Start(
+        FROM_HERE, *cumulative_buyer_timeout,
+        base::BindOnce(&BuyerHelper::OnTimeout, base::Unretained(this)));
+  }
+
+  // Called when the `cumulative_buyer_timeout_timer_` expires.
+  void OnTimeout() {
+    // If there are no outstanding bids, then the timer should not still be
+    // running.
+    DCHECK_GT(num_outstanding_bids_, 0);
+
+    // Assemble a list of interest groups that haven't bid yet - have to do
+    // this, since calling OnGenerateBidCompleteInternal() on the last
+    // incomplete bid may delete `this`, if it ends the auction.
+    std::list<BidState*> pending_bids;
+    for (auto& bid_state : bid_states_) {
+      if (!bid_state->worklet_handle) {
+        continue;
+      }
+      // Put the IGs that have received signals first, since cancelling the last
+      // bid that has not received signals could cause a bid that has received
+      // signals to start running Javascript.
+      if (bid_state->bidding_signals_received) {
+        pending_bids.push_front(bid_state.get());
+      } else {
+        pending_bids.push_back(bid_state.get());
+      }
+    }
+
+    for (auto* pending_bid : pending_bids) {
+      // Fail bids individually, with errors. This does potentially do extra
+      // work over just failing the entire auction directly, but ensures there's
+      // a single failure path, reducing the chance of future breakages.
+      OnFatalError(
+          pending_bid, /*errors=*/{base::StrCat(
+              {pending_bid->bidder->interest_group.bidding_url->spec(),
+               " perBuyerCumulativeTimeout exceeded during bid generation."})});
+    }
+  }
+
   // Calls SendPendingSignalsRequests() for the BidderWorklet of `bid_state`,
   // if it hasn't been destroyed. This is done asynchronously, so that
   // BidStates that share a BidderWorklet all call GenerateBid() before this
@@ -1256,6 +1398,17 @@
                               BidState*>
       generate_bid_client_receiver_set_;
 
+  // Set to true once a single bidder worklet has been received (and thus, since
+  // all bidder worklets managed by a BuyerHelper use the same process, `this`
+  // is no longer blocked waiting on other bidders to complete).
+  bool bidder_process_received_ = false;
+
+  // Timer for applying the perBidderCumulativeTimeout, if one is applicable.
+  // Starts once `bidder_process_received_` and
+  // `auction_->config_promises_resolved_` are true, if
+  // `cumulative_buyer_timeout_` is not nullopt.
+  base::OneShotTimer cumulative_buyer_timeout_timer_;
+
   int num_outstanding_bidding_signals_received_calls_ = 0;
   int num_outstanding_bids_ = 0;
 
@@ -1620,8 +1773,6 @@
 }
 
 void InterestGroupAuction::ClosePipes() {
-  // This is needed in addition to closing worklet pipes since the callbacks
-  // passed to Mojo pipes this class doesn't own aren't cancellable.
   weak_ptr_factory_.InvalidateWeakPtrs();
 
   score_ad_receivers_.Clear();
@@ -1747,7 +1898,7 @@
 
 bool InterestGroupAuction::ReportPaBuyersValueIfAllowed(
     const blink::InterestGroup& interest_group,
-    blink::InterestGroup::SellerCapabilities capability,
+    blink::SellerCapabilities capability,
     blink::AuctionConfig::NonSharedParams::BuyerReportType buyer_report_type,
     int value) {
   if (!CanReportPaBuyersValue(interest_group, capability, config_->seller)) {
@@ -1989,8 +2140,7 @@
     const blink::InterestGroup& interest_group,
     size_t count) {
   return ReportPaBuyersValueIfAllowed(
-      interest_group,
-      blink::InterestGroup::SellerCapabilities::kInterestGroupCounts,
+      interest_group, blink::SellerCapabilities::kInterestGroupCounts,
       blink::AuctionConfig::NonSharedParams::BuyerReportType::
           kInterestGroupCount,
       count);
@@ -2000,29 +2150,28 @@
     const blink::InterestGroup& interest_group,
     size_t count) {
   return ReportPaBuyersValueIfAllowed(
-      interest_group,
-      blink::InterestGroup::SellerCapabilities::kInterestGroupCounts,
+      interest_group, blink::SellerCapabilities::kInterestGroupCounts,
       blink::AuctionConfig::NonSharedParams::BuyerReportType::kBidCount, count);
 }
 
 void InterestGroupAuction::ReportTrustedSignalsFetchLatency(
     const blink::InterestGroup& interest_group,
     base::TimeDelta trusted_signals_fetch_latency) {
-  ReportPaBuyersValueIfAllowed(
-      interest_group, blink::InterestGroup::SellerCapabilities::kLatencyStats,
-      blink::AuctionConfig::NonSharedParams::BuyerReportType::
-          kTotalSignalsFetchLatency,
-      trusted_signals_fetch_latency.InMilliseconds());
+  ReportPaBuyersValueIfAllowed(interest_group,
+                               blink::SellerCapabilities::kLatencyStats,
+                               blink::AuctionConfig::NonSharedParams::
+                                   BuyerReportType::kTotalSignalsFetchLatency,
+                               trusted_signals_fetch_latency.InMilliseconds());
 }
 
 void InterestGroupAuction::ReportBiddingLatency(
     const blink::InterestGroup& interest_group,
     base::TimeDelta bidding_latency) {
-  ReportPaBuyersValueIfAllowed(
-      interest_group, blink::InterestGroup::SellerCapabilities::kLatencyStats,
-      blink::AuctionConfig::NonSharedParams::BuyerReportType::
-          kTotalGenerateBidLatency,
-      bidding_latency.InMilliseconds());
+  ReportPaBuyersValueIfAllowed(interest_group,
+                               blink::SellerCapabilities::kLatencyStats,
+                               blink::AuctionConfig::NonSharedParams::
+                                   BuyerReportType::kTotalGenerateBidLatency,
+                               bidding_latency.InMilliseconds());
 }
 
 base::flat_set<std::string> InterestGroupAuction::GetKAnonKeysToJoin() const {
@@ -2753,28 +2902,10 @@
     leader_info.highest_scoring_other_bid = bid_value;
 }
 
-absl::optional<base::TimeDelta> InterestGroupAuction::PerBuyerTimeout(
-    const BidState* state) {
-  DCHECK(!config_->non_shared_params.buyer_timeouts.is_promise());
-  const auto& per_buyer_timeouts =
-      config_->non_shared_params.buyer_timeouts.value().per_buyer_timeouts;
-  if (per_buyer_timeouts.has_value()) {
-    auto it = per_buyer_timeouts->find(state->bidder->interest_group.owner);
-    if (it != per_buyer_timeouts->end()) {
-      return std::min(it->second, kMaxTimeout);
-    }
-  }
-  const auto& all_buyers_timeout =
-      config_->non_shared_params.buyer_timeouts.value().all_buyers_timeout;
-  if (all_buyers_timeout.has_value())
-    return std::min(all_buyers_timeout.value(), kMaxTimeout);
-  return absl::nullopt;
-}
-
 absl::optional<base::TimeDelta> InterestGroupAuction::SellerTimeout() {
   if (config_->non_shared_params.seller_timeout.has_value()) {
     return std::min(config_->non_shared_params.seller_timeout.value(),
-                    kMaxTimeout);
+                    kMaxPerBuyerTimeout);
   }
   return absl::nullopt;
 }
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index 12d34cb..5168506 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -779,7 +779,6 @@
                                    const url::Origin* owner,
                                    LeaderInfo& leader_info);
 
-  absl::optional<base::TimeDelta> PerBuyerTimeout(const BidState* state);
   absl::optional<base::TimeDelta> SellerTimeout();
 
   // If AllBidsScored() is true, completes the bidding and scoring phase.
@@ -857,7 +856,7 @@
   // the server.
   bool ReportPaBuyersValueIfAllowed(
       const blink::InterestGroup& interest_group,
-      blink::InterestGroup::SellerCapabilities capability,
+      blink::SellerCapabilities capability,
       blink::AuctionConfig::NonSharedParams::BuyerReportType buyer_report_type,
       int value);
 
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index c4fca50f..f528463c 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -141,14 +141,12 @@
 }
 
 base::Value::List SellerCapabilitiesToList(
-    blink::InterestGroup::SellerCapabilitiesType capabilities) {
+    blink::SellerCapabilitiesType capabilities) {
   base::Value::List list;
-  for (blink::InterestGroup::SellerCapabilities capability : capabilities) {
-    if (capability ==
-        blink::InterestGroup::SellerCapabilities::kInterestGroupCounts) {
+  for (blink::SellerCapabilities capability : capabilities) {
+    if (capability == blink::SellerCapabilities::kInterestGroupCounts) {
       list.Append("interestGroupCounts");
-    } else if (capability ==
-               blink::InterestGroup::SellerCapabilities::kLatencyStats) {
+    } else if (capability == blink::SellerCapabilities::kLatencyStats) {
       list.Append("latencyStats");
     } else {
       ADD_FAILURE() << "Unknown seller capability "
@@ -160,9 +158,8 @@
 
 base::Value::Dict SellerCapabilitiesToDict(
     const absl::optional<
-        base::flat_map<url::Origin,
-                       blink::InterestGroup::SellerCapabilitiesType>>& map,
-    blink::InterestGroup::SellerCapabilitiesType all_sellers_capabilities) {
+        base::flat_map<url::Origin, blink::SellerCapabilitiesType>>& map,
+    blink::SellerCapabilitiesType all_sellers_capabilities) {
   base::Value::Dict dict;
   if (map) {
     for (const auto& [origin, capabilities] : *map) {
@@ -2649,10 +2646,9 @@
                     /*name=*/"cars")
                     .SetSellerCapabilities(
                         {{{url::Origin::Create(GURL("https://example.test")),
-                           blink::InterestGroup::SellerCapabilities::
-                               kInterestGroupCounts}}})
+                           blink::SellerCapabilities::kInterestGroupCounts}}})
                     .SetAllSellerCapabilities(
-                        blink::InterestGroup::SellerCapabilities::kLatencyStats)
+                        blink::SellerCapabilities::kLatencyStats)
                     .Build()));
 
   WaitForAccessObserved({});
@@ -2661,12 +2657,12 @@
   ASSERT_EQ(groups.size(), 1u);
   const blink::InterestGroup& group = groups[0].interest_group;
   EXPECT_EQ(group.all_sellers_capabilities,
-            blink::InterestGroup::SellerCapabilities::kLatencyStats);
+            blink::SellerCapabilities::kLatencyStats);
   ASSERT_TRUE(group.seller_capabilities);
   ASSERT_EQ(group.seller_capabilities->size(), 1u);
   EXPECT_EQ(group.seller_capabilities->at(
                 url::Origin::Create(GURL("https://example.test"))),
-            blink::InterestGroup::SellerCapabilities::kInterestGroupCounts);
+            blink::SellerCapabilities::kInterestGroupCounts);
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -2687,8 +2683,7 @@
           /*priority_signals_overrides=*/absl::nullopt,
           /*seller_capabilities=*/{},
           /*all_sellers_capabilities=*/
-          blink::InterestGroup::SellerCapabilities::
-              kLatencyStats, /*execution_mode=*/
+          blink::SellerCapabilities::kLatencyStats, /*execution_mode=*/
           blink::InterestGroup::ExecutionMode::kCompatibilityMode,
           /*bidding_url=*/absl::nullopt,
           /*bidding_wasm_helper_url=*/absl::nullopt,
@@ -7690,7 +7685,7 @@
     sellerTimeout: 200,
     perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}},
     perBuyerTimeouts: {$4: 110, $5: 120, '*': 150},
-    perBuyerCumulativeTimeouts: {$4: 130, $5: 140, '*': 160},
+    perBuyerCumulativeTimeouts: {$4: 13000, $5: 14000, '*': 16000},
     perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}}
   });
 })())",
@@ -7821,7 +7816,7 @@
     sellerTimeout: 200,
     perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}},
     perBuyerTimeouts: {$4: 110, $5: 120, '*': 150},
-    perBuyerCumulativeTimeouts: {$4: 130, $5: 140, '*': 160},
+    perBuyerCumulativeTimeouts: {$4: 13000, $5: 14000, '*': 16000},
     perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}}
   });
 })())",
@@ -7999,7 +7994,7 @@
     sellerTimeout: 300,
     perBuyerSignals: maybePromise({$8: ["top-level buyer signals"]}),
     perBuyerTimeouts: maybePromise({$8: 110, '*': 150}),
-    perBuyerCumulativeTimeouts: maybePromise({$8: 111, '*': 151}),
+    perBuyerCumulativeTimeouts: maybePromise({$8: 11100, '*': 15100}),
     perBuyerPrioritySignals: {'*': {foo: 3}},
     componentAuctions: [{
       seller: $5,
@@ -8012,7 +8007,7 @@
       sellerTimeout: 200,
       perBuyerSignals: maybePromise({$8: ["component buyer signals"]}),
       perBuyerTimeouts: maybePromise({$8: 200}),
-      perBuyerCumulativeTimeouts: maybePromise({$8: 201}),
+      perBuyerCumulativeTimeouts: maybePromise({$8: 20100}),
       perBuyerPrioritySignals: {$8: {bar: 1}, '*': {BaZ: -2}},
     }],
   });
@@ -8671,13 +8666,13 @@
   let ok = 0;
   for (key in perBuyerCumulativeTimeouts) {
     if (key.startsWith("https://a.test") &&
-        perBuyerCumulativeTimeouts[key] === 70) {
+        perBuyerCumulativeTimeouts[key] === 7000) {
       ++ok;
     } else if (key === "https://c.test" &&
-        perBuyerCumulativeTimeouts[key] === 80) {
+        perBuyerCumulativeTimeouts[key] === 8000) {
       ++ok;
     } else if (key === '*' &&
-        perBuyerCumulativeTimeouts[key] === 76) {
+        perBuyerCumulativeTimeouts[key] === 7600) {
       ++ok;
     } else {
       throw 'Wrong key in perCumulativeBuyerTimeouts ' +
@@ -8733,7 +8728,7 @@
     }),
     perBuyerCumulativeTimeouts: new Promise((resolve, reject) => {
       setTimeout(
-          () => { resolve({$1: 70, 'https://c.test': 80, '*': 76}); }, 1)
+          () => { resolve({$1: 7000, 'https://c.test': 8000, '*': 7600}); }, 1)
     })
   });
 })())",
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index b738851..b01a04a 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -51,7 +51,7 @@
 
 using auction_worklet::mojom::BiddingBrowserSignalsPtr;
 using auction_worklet::mojom::PreviousWinPtr;
-using SellerCapabilitiesType = blink::InterestGroup::SellerCapabilitiesType;
+using SellerCapabilitiesType = blink::SellerCapabilitiesType;
 
 const base::FilePath::CharType kDatabasePath[] =
     FILE_PATH_LITERAL("InterestGroups");
diff --git a/content/browser/interest_group/interest_group_storage_unittest.cc b/content/browser/interest_group/interest_group_storage_unittest.cc
index 10d8cbb4..90601c41 100644
--- a/content/browser/interest_group/interest_group_storage_unittest.cc
+++ b/content/browser/interest_group/interest_group_storage_unittest.cc
@@ -43,8 +43,8 @@
 using testing::Field;
 using testing::UnorderedElementsAre;
 using testing::UnorderedElementsAreArray;
-using SellerCapabilities = blink::InterestGroup::SellerCapabilities;
-using SellerCapabilitiesType = blink::InterestGroup::SellerCapabilitiesType;
+using SellerCapabilities = blink::SellerCapabilities;
+using SellerCapabilitiesType = blink::SellerCapabilitiesType;
 
 class InterestGroupStorageTest : public testing::Test {
  public:
diff --git a/content/browser/interest_group/interest_group_update.h b/content/browser/interest_group/interest_group_update.h
index 1677b3f..cb84663 100644
--- a/content/browser/interest_group/interest_group_update.h
+++ b/content/browser/interest_group/interest_group_update.h
@@ -38,11 +38,9 @@
   // old overrides around. Keys mapped to nullopt are deleted.
   absl::optional<base::flat_map<std::string, absl::optional<double>>>
       priority_signals_overrides;
-  absl::optional<
-      base::flat_map<url::Origin, blink::InterestGroup::SellerCapabilitiesType>>
+  absl::optional<base::flat_map<url::Origin, blink::SellerCapabilitiesType>>
       seller_capabilities;
-  absl::optional<blink::InterestGroup::SellerCapabilitiesType>
-      all_sellers_capabilities;
+  absl::optional<blink::SellerCapabilitiesType> all_sellers_capabilities;
   absl::optional<blink::InterestGroup::ExecutionMode> execution_mode;
   absl::optional<GURL> bidding_url;
   absl::optional<GURL> bidding_wasm_helper_url;
diff --git a/content/browser/interest_group/interest_group_update_manager.cc b/content/browser/interest_group/interest_group_update_manager.cc
index 196d1f3..04604459 100644
--- a/content/browser/interest_group/interest_group_update_manager.cc
+++ b/content/browser/interest_group/interest_group_update_manager.cc
@@ -176,24 +176,21 @@
   if (!maybe_dict->is_dict())
     return false;
 
-  std::vector<
-      std::pair<url::Origin, blink::InterestGroup::SellerCapabilitiesType>>
+  std::vector<std::pair<url::Origin, blink::SellerCapabilitiesType>>
       seller_capabilities_vec;
   for (const std::pair<const std::string&, const base::Value&> pair :
        maybe_dict->GetDict()) {
     if (!pair.second.is_list())
       return false;
-    blink::InterestGroup::SellerCapabilitiesType capabilities;
+    blink::SellerCapabilitiesType capabilities;
     for (const base::Value& maybe_capability : pair.second.GetList()) {
       if (!maybe_capability.is_string())
         return false;
       const std::string& capability = maybe_capability.GetString();
       if (capability == "interestGroupCounts") {
-        capabilities.Put(
-            blink::InterestGroup::SellerCapabilities::kInterestGroupCounts);
+        capabilities.Put(blink::SellerCapabilities::kInterestGroupCounts);
       } else if (capability == "latencyStats") {
-        capabilities.Put(
-            blink::InterestGroup::SellerCapabilities::kLatencyStats);
+        capabilities.Put(blink::SellerCapabilities::kLatencyStats);
       } else {
         return false;
       }
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index 43df28f..35521b4 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -237,7 +237,8 @@
             base::TimeTicks() /* renderer_before_unload_end */,
             absl::nullopt /* web_bundle_token */,
             blink::mojom::NavigationInitiatorActivationAndAdStatus::
-                kDidNotStartWithTransientActivation);
+                kDidNotStartWithTransientActivation,
+            false /* is_container_initiated */);
 
     auto common_params = blink::CreateCommonNavigationParams();
     common_params->url = url;
diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc
index 3df6640..229b08c 100644
--- a/content/browser/loader/navigation_url_loader_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_unittest.cc
@@ -98,7 +98,8 @@
             base::TimeTicks() /* renderer_before_unload_end */,
             absl::nullopt /* web_bundle_token */,
             blink::mojom::NavigationInitiatorActivationAndAdStatus::
-                kDidNotStartWithTransientActivation);
+                kDidNotStartWithTransientActivation,
+            false /* is_container_initiated */);
     auto common_params = blink::CreateCommonNavigationParams();
     common_params->url = url;
     common_params->initiator_origin = url::Origin::Create(url);
diff --git a/content/browser/loader/object_navigation_fallback_body_loader.cc b/content/browser/loader/object_navigation_fallback_body_loader.cc
index f7b0f41..57405999 100644
--- a/content/browser/loader/object_navigation_fallback_body_loader.cc
+++ b/content/browser/loader/object_navigation_fallback_body_loader.cc
@@ -10,7 +10,6 @@
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
-#include "content/browser/loader/resource_timing_utils.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
@@ -32,35 +31,15 @@
 
 namespace content {
 
-namespace {
-
-std::string ExtractServerTimingValueIfNeeded(
-    const network::mojom::URLResponseHead& response_head) {
-  std::string value;
-  if (!response_head.timing_allow_passed)
-    return value;
-
-  // Note: the renderer will be responsible for parsing the actual server
-  // timing values.
-  response_head.headers->GetNormalizedHeader("Server-Timing", &value);
-  return value;
-}
-
-}  // namespace
-
 NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(ObjectNavigationFallbackBodyLoader);
 
 // static
 void ObjectNavigationFallbackBodyLoader::CreateAndStart(
     NavigationRequest& navigation_request,
-    const blink::mojom::CommonNavigationParams& common_params,
-    const blink::mojom::CommitNavigationParams& commit_params,
-    const network::mojom::URLResponseHead& response_head,
     mojo::ScopedDataPipeConsumerHandle response_body,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     base::OnceClosure completion_closure) {
   // This should only be called for HTTP errors.
-  DCHECK(response_head.headers);
   RenderFrameHostImpl* render_frame_host =
       navigation_request.frame_tree_node()->current_frame_host();
   // A frame owned by <object> should always have a parent.
@@ -68,25 +47,15 @@
   // It's safe to snapshot the parent origin in the calculation here; if the
   // parent frame navigates, `render_frame_host_` will be deleted, which
   // triggers deletion of `this`, cancelling all remaining work.
-  blink::mojom::ResourceTimingInfoPtr timing_info =
-      GenerateResourceTimingForNavigation(
-          render_frame_host->GetParent()->GetLastCommittedOrigin(),
-          common_params, commit_params, response_head);
-  std::string server_timing_value =
-      ExtractServerTimingValueIfNeeded(response_head);
-
-  CreateForNavigationHandle(
-      navigation_request, std::move(timing_info),
-      std::move(server_timing_value), std::move(response_body),
-      std::move(url_loader_client_endpoints), std::move(completion_closure));
+  CreateForNavigationHandle(navigation_request, std::move(response_body),
+                            std::move(url_loader_client_endpoints),
+                            std::move(completion_closure));
 }
 
 ObjectNavigationFallbackBodyLoader::~ObjectNavigationFallbackBodyLoader() {}
 
 ObjectNavigationFallbackBodyLoader::ObjectNavigationFallbackBodyLoader(
     NavigationHandle& navigation_handle,
-    blink::mojom::ResourceTimingInfoPtr timing_info,
-    std::string server_timing_value,
     mojo::ScopedDataPipeConsumerHandle response_body,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     base::OnceClosure completion_closure)
@@ -98,8 +67,6 @@
       response_body_drainer_(
           std::make_unique<mojo::DataPipeDrainer>(this,
                                                   std::move(response_body))),
-      timing_info_(std::move(timing_info)),
-      server_timing_value_(std::move(server_timing_value)),
       completion_closure_(std::move(completion_closure)) {
   // Unretained is safe; `url_loader_` is owned by `this` and will not dispatch
   // callbacks after it is destroyed.
@@ -165,25 +132,9 @@
   // `completion_closure_` will delete the NavigationRequest, which will delete
   // `this`.
   base::ScopedClosureRunner cleanup(std::move(completion_closure_));
-
-  timing_info_->response_end = status.completion_time;
-  timing_info_->encoded_body_size = status.encoded_body_length;
-  timing_info_->decoded_body_size = status.decoded_body_length;
-
-  RenderFrameHostManager* render_manager =
-      navigation_request_->frame_tree_node()->render_manager();
-  if (RenderFrameProxyHost* proxy = render_manager->GetProxyToParent()) {
-    if (proxy->is_render_frame_proxy_live()) {
-      proxy->GetAssociatedRemoteFrame()
-          ->RenderFallbackContentWithResourceTiming(std::move(timing_info_),
-                                                    server_timing_value_);
-    }
-  } else {
-    render_manager->current_frame_host()
-        ->GetAssociatedLocalFrame()
-        ->RenderFallbackContentWithResourceTiming(std::move(timing_info_),
-                                                  server_timing_value_);
-  }
+  navigation_request_->AddResourceTimingEntryForFailedSubframeNavigation(
+      status);
+  navigation_request_->RenderFallbackContentForObjectTag();
 }
 
 void ObjectNavigationFallbackBodyLoader::OnDataAvailable(const void* data,
diff --git a/content/browser/loader/object_navigation_fallback_body_loader.h b/content/browser/loader/object_navigation_fallback_body_loader.h
index 34acb77..5568126 100644
--- a/content/browser/loader/object_navigation_fallback_body_loader.h
+++ b/content/browser/loader/object_navigation_fallback_body_loader.h
@@ -21,12 +21,6 @@
 #include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h"
 #include "third_party/blink/public/mojom/timing/resource_timing.mojom.h"
 
-namespace network {
-namespace mojom {
-class URLResponseHead;
-}  // namespace mojom
-}  // namespace network
-
 namespace content {
 
 class NavigationRequest;
@@ -59,9 +53,6 @@
       public network::mojom::URLLoaderClient,
       public mojo::DataPipeDrainer::Client {
  public:
-  // `common_params, `commit_params`, and `response_head`  are used to
-  // (partially) generate the resource timing info.
-  //
   // `response_body` and `url_loader_client_endpoints` are used to drain the
   // responise body and calculate the body / data size needed for the
   // performance entry.
@@ -75,9 +66,6 @@
   // response body is successfully loaded.
   static void CreateAndStart(
       NavigationRequest& navigation_request,
-      const blink::mojom::CommonNavigationParams& common_params,
-      const blink::mojom::CommitNavigationParams& commit_params,
-      const network::mojom::URLResponseHead& response_head,
       mojo::ScopedDataPipeConsumerHandle response_body,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       base::OnceClosure completion_closure);
@@ -90,8 +78,6 @@
 
   ObjectNavigationFallbackBodyLoader(
       NavigationHandle& navigation_handle,
-      blink::mojom::ResourceTimingInfoPtr timing_info,
-      std::string server_timing_value,
       mojo::ScopedDataPipeConsumerHandle response_body,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       base::OnceClosure completion_closure);
@@ -123,8 +109,6 @@
   // `response_body_drainer_` will be reset to null when the response body is
   // completely drained.
   std::unique_ptr<mojo::DataPipeDrainer> response_body_drainer_;
-  blink::mojom::ResourceTimingInfoPtr timing_info_;
-  std::string server_timing_value_;
   base::OnceClosure completion_closure_;
 };
 
diff --git a/content/browser/loader/resource_timing_utils.cc b/content/browser/loader/resource_timing_utils.cc
deleted file mode 100644
index 749756ba9a..0000000
--- a/content/browser/loader/resource_timing_utils.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/loader/resource_timing_utils.h"
-
-#include <string>
-#include <utility>
-
-#include "base/containers/contains.h"
-#include "net/http/http_response_info.h"
-#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
-#include "third_party/blink/public/mojom/timing/resource_timing.mojom.h"
-#include "url/gurl.h"
-#include "url/url_util.h"
-
-namespace {
-
-// Implements the TimingAllowOrigin check
-// This logic is duplicated from Performance::AllowsTimingRedirect(). Ensure
-// that any changes are synced between both copies.
-bool IsCrossOriginResponseOrHasCrossOriginRedirects(
-    const url::Origin& parent_origin,
-    const blink::mojom::CommonNavigationParams& common_params,
-    const blink::mojom::CommitNavigationParams& commit_params) {
-  DCHECK_EQ(commit_params.redirect_infos.size(),
-            commit_params.redirect_response.size());
-  for (const auto& info : commit_params.redirect_infos) {
-    if (!parent_origin.IsSameOriginWith(info.new_url)) {
-      return true;
-    }
-  }
-
-  return !parent_origin.IsSameOriginWith(common_params.url);
-}
-
-}  // namespace
-
-namespace content {
-
-// This logic is duplicated from blink::CreateResourceTimingInfo(). Ensure
-// that any changes are synced between both copies.
-blink::mojom::ResourceTimingInfoPtr GenerateResourceTimingForNavigation(
-    const url::Origin& parent_origin,
-    const blink::mojom::CommonNavigationParams& common_params,
-    const blink::mojom::CommitNavigationParams& commit_params,
-    const network::mojom::URLResponseHead& response_head) {
-  // TODO(dcheng): There should be a Blink helper for populating the timing info
-  // that's exposed in //third_party/blink/common. This would allow a lot of the
-  // boilerplate to be shared.
-
-  auto timing_info = blink::mojom::ResourceTimingInfo::New();
-  const GURL& initial_url = !commit_params.original_url.is_empty()
-                                ? commit_params.original_url
-                                : common_params.url;
-  timing_info->name = initial_url.spec();
-  timing_info->start_time = common_params.navigation_start;
-  timing_info->allow_timing_details = response_head.timing_allow_passed;
-
-  // Only expose the response code when we are same origin and without
-  // cross-origin redirects
-  // https://fetch.spec.whatwg.org/#ref-for-concept-response-status%E2%91%A6
-  if (!IsCrossOriginResponseOrHasCrossOriginRedirects(
-          parent_origin, common_params, commit_params)) {
-    timing_info->response_status = commit_params.http_response_code;
-  }
-
-  // https://fetch.spec.whatwg.org/#create-an-opaque-timing-info
-  if (!timing_info->allow_timing_details) {
-    return timing_info;
-  }
-
-  timing_info->alpn_negotiated_protocol =
-      response_head.alpn_negotiated_protocol;
-  timing_info->connection_info = net::HttpResponseInfo::ConnectionInfoToString(
-      response_head.connection_info);
-
-  // If there's no received headers end time, don't set load timing. This is the
-  // case for non-HTTP requests, requests that don't go over the wire, and
-  // certain error cases.
-  // TODO(dcheng): Is it actually possible to hit this path if
-  // `response_head.headers` is populated?
-  if (!response_head.load_timing.receive_headers_end.is_null()) {
-    timing_info->timing = response_head.load_timing;
-  }
-  // `response_end` will be populated after loading the body.
-
-  DCHECK_EQ(commit_params.redirect_infos.size(),
-            commit_params.redirect_response.size());
-
-  if (!commit_params.redirect_infos.empty()) {
-    timing_info->last_redirect_end_time =
-        commit_params.redirect_response.back()->load_timing.receive_headers_end;
-  } else {
-    timing_info->last_redirect_end_time = base::TimeTicks();
-  }
-  // The final value for `encoded_body_size` and `decoded_body_size` will be
-  // populated after loading the body.
-  timing_info->did_reuse_connection = response_head.load_timing.socket_reused;
-  // Use url::Origin to handle cases like blob:https://.
-  timing_info->is_secure_transport = base::Contains(
-      url::GetSecureSchemes(), url::Origin::Create(common_params.url).scheme());
-  timing_info->allow_negative_values = false;
-  return timing_info;
-}
-}  // namespace content
diff --git a/content/browser/loader/resource_timing_utils.h b/content/browser/loader/resource_timing_utils.h
deleted file mode 100644
index 764495b..0000000
--- a/content/browser/loader/resource_timing_utils.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_LOADER_RESOURCE_TIMING_UTILS_H_
-#define CONTENT_BROWSER_LOADER_RESOURCE_TIMING_UTILS_H_
-
-#include "services/network/public/mojom/url_response_head.mojom-forward.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-forward.h"
-#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h"
-#include "third_party/blink/public/mojom/timing/resource_timing.mojom-forward.h"
-#include "url/origin.h"
-
-namespace content {
-
-blink::mojom::ResourceTimingInfoPtr GenerateResourceTimingForNavigation(
-    const url::Origin& parent_origin,
-    const blink::mojom::CommonNavigationParams& common_params,
-    const blink::mojom::CommitNavigationParams& commit_params,
-    const network::mojom::URLResponseHead& response_head);
-
-}  // namespace content
-
-#endif
diff --git a/content/browser/renderer_host/back_forward_cache_impl.h b/content/browser/renderer_host/back_forward_cache_impl.h
index a3f2b7c..92d1ec2 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.h
+++ b/content/browser/renderer_host/back_forward_cache_impl.h
@@ -162,6 +162,11 @@
       stored_page_->SetDelegate(delegate);
     }
 
+    void SetViewTransitionState(
+        absl::optional<blink::ViewTransitionState> view_transition_state) {
+      stored_page_->SetViewTransitionState(std::move(view_transition_state));
+    }
+
     // The main document being stored.
     RenderFrameHostImpl* render_frame_host() {
       return stored_page_->render_frame_host();
diff --git a/content/browser/renderer_host/ipc_utils.cc b/content/browser/renderer_host/ipc_utils.cc
index 3a2dab5..aa8a0028 100644
--- a/content/browser/renderer_host/ipc_utils.cc
+++ b/content/browser/renderer_host/ipc_utils.cc
@@ -171,6 +171,16 @@
     return false;
   }
 
+  if (params->is_container_initiated) {
+    if (!current_rfh->GetParent() ||
+        (current_rfh->GetParent()->GetFrameToken() !=
+         params->initiator_frame_token)) {
+      mojo::ReportBadMessage(
+          "container initiated navigation from non-parent process");
+      return false;
+    }
+  }
+
   // Verification succeeded.
   return true;
 }
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 358540b..b74775eb 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -2692,7 +2692,8 @@
     base::TimeTicks navigation_start_time,
     bool is_embedder_initiated_fenced_frame_navigation,
     bool is_unfenced_top_navigation,
-    bool force_new_browsing_instance) {
+    bool force_new_browsing_instance,
+    bool is_container_initiated) {
   if (is_renderer_initiated)
     DCHECK(initiator_origin.has_value());
 
@@ -2817,7 +2818,7 @@
           false /* has_user_gesture */, std::move(source_location),
           ReloadType::NONE, entry.get(), frame_entry.get(),
           navigation_start_time, is_embedder_initiated_fenced_frame_navigation,
-          is_unfenced_top_navigation);
+          is_unfenced_top_navigation, is_container_initiated);
 
   if (!request)
     return;
@@ -3760,7 +3761,8 @@
     FrameNavigationEntry* frame_entry,
     base::TimeTicks navigation_start_time,
     bool is_embedder_initiated_fenced_frame_navigation,
-    bool is_unfenced_top_navigation) {
+    bool is_unfenced_top_navigation,
+    bool is_container_initiated) {
   DCHECK_EQ(-1, GetIndexOfEntry(entry));
   DCHECK(frame_entry);
   // All renderer-initiated navigations must have an initiator_origin.
@@ -3931,7 +3933,8 @@
       params.is_form_submission,
       params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr,
       params.impression, params.initiator_activation_and_ad_status,
-      params.is_pdf, is_embedder_initiated_fenced_frame_navigation);
+      params.is_pdf, is_embedder_initiated_fenced_frame_navigation,
+      is_container_initiated);
   navigation_request->set_from_download_cross_origin_redirect(
       params.from_download_cross_origin_redirect);
   navigation_request->set_force_new_browsing_instance(
diff --git a/content/browser/renderer_host/navigation_controller_impl.h b/content/browser/renderer_host/navigation_controller_impl.h
index ebb6afd8..ccdf116 100644
--- a/content/browser/renderer_host/navigation_controller_impl.h
+++ b/content/browser/renderer_host/navigation_controller_impl.h
@@ -227,7 +227,8 @@
       base::TimeTicks navigation_start_time,
       bool is_embedder_initiated_fenced_frame_navigation = false,
       bool is_unfenced_top_navigation = false,
-      bool force_new_browsing_instance = false);
+      bool force_new_browsing_instance = false,
+      bool is_container_initiated = false);
 
   // Navigates to the history entry associated with the given navigation API
   // |key|. Searches |entries_| for a FrameNavigationEntry associated with
@@ -653,7 +654,8 @@
       FrameNavigationEntry* frame_entry,
       base::TimeTicks navigation_start_time,
       bool is_embedder_initiated_fenced_frame_navigation = false,
-      bool is_unfenced_top_navigation = false);
+      bool is_unfenced_top_navigation = false,
+      bool is_container_initiated = false);
 
   // Creates and returns a NavigationRequest for a navigation to |entry|. Will
   // return nullptr if the parameters are invalid and the navigation cannot
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index b50193b..2717fd4 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -53,7 +53,6 @@
 #include "content/browser/loader/navigation_early_hints_manager.h"
 #include "content/browser/loader/navigation_url_loader.h"
 #include "content/browser/loader/object_navigation_fallback_body_loader.h"
-#include "content/browser/loader/resource_timing_utils.h"
 #include "content/browser/navigation_or_document_handle.h"
 #include "content/browser/network/cross_origin_embedder_policy_reporter.h"
 #include "content/browser/network/cross_origin_opener_policy_reporter.h"
@@ -79,6 +78,7 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/runtime_feature_state/runtime_feature_state_document_data.h"
 #include "content/browser/scoped_active_url.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_main_resource_handle.h"
@@ -140,6 +140,7 @@
 #include "services/network/public/cpp/web_sandbox_flags.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/supports_loading_mode.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "services/network/public/mojom/url_response_head.mojom-shared.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "services/network/public/mojom/web_client_hints_types.mojom-shared.h"
@@ -169,6 +170,7 @@
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/loader/mixed_content.mojom.h"
 #include "third_party/blink/public/mojom/loader/transferrable_url_loader.mojom.h"
+#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-shared.h"
 #include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
 #include "third_party/blink/public/mojom/navigation/prefetched_signed_exchange_info.mojom.h"
 #include "third_party/blink/public/mojom/runtime_feature_state/runtime_feature_state.mojom.h"
@@ -1098,7 +1100,8 @@
     blink::mojom::NavigationInitiatorActivationAndAdStatus
         initiator_activation_and_ad_status,
     bool is_pdf,
-    bool is_embedder_initiated_fenced_frame_navigation) {
+    bool is_embedder_initiated_fenced_frame_navigation,
+    bool is_container_initiated) {
   TRACE_EVENT1("navigation", "NavigationRequest::Create", "browser_initiated",
                browser_initiated);
 
@@ -1128,7 +1131,8 @@
       nullptr /* trust_token_params */, impression,
       base::TimeTicks() /* renderer_before_unload_start */,
       base::TimeTicks() /* renderer_before_unload_end */,
-      std::move(web_bundle_token_params), initiator_activation_and_ad_status);
+      std::move(web_bundle_token_params), initiator_activation_and_ad_status,
+      is_container_initiated);
 
   // Shift-Reload forces bypassing caches and service workers.
   if (common_params->navigation_type ==
@@ -1453,13 +1457,21 @@
   } else {
     // Otherwise we need to derive the top_level_site and ancestor_chain_bit.
     net::SchemefulSite top_level_site(top_level_origin);
+
+    blink::mojom::AncestorChainBit ancestor_chain_bit =
+        blink::mojom::AncestorChainBit::kSameSite;
+    if (render_frame_host->ComputeSiteForCookies().IsNull() ||
+        net::SchemefulSite(origin) != top_level_site ||
+        !top_level_site.opaque() || origin.opaque()) {
+      ancestor_chain_bit = blink::mojom::AncestorChainBit::kCrossSite;
+    }
+
+    // Because this is a synchronous commit from the renderer the RFH won't
+    // change meaning we can always query the main frame RFH for the status of
+    // storage partitioning.
     navigation_request->commit_params_->storage_key = blink::StorageKey::Create(
-        origin, top_level_site,
-        render_frame_host->ComputeSiteForCookies().IsNull() ||
-                net::SchemefulSite(origin) != top_level_site ||
-                origin.opaque() || top_level_site.opaque()
-            ? blink::mojom::AncestorChainBit::kCrossSite
-            : blink::mojom::AncestorChainBit::kSameSite);
+        origin, top_level_site, ancestor_chain_bit,
+        render_frame_host->IsMainFrameThirdPartyStoragePartitioningEnabled());
   }
   navigation_request->commit_params_->session_storage_key =
       frame_tree_node->frame_tree().GetSessionStorageKey(
@@ -1623,6 +1635,24 @@
     if (initiator_rfh)
       initiator_document_ = initiator_rfh->GetWeakDocumentPtr();
   }
+
+  // Spec: https://github.com/whatwg/html/issues/8846
+  // We only allow the parent to access a subframe resource timing if the
+  // navigation is container-initiated, e.g. iframe changed src.
+  if (begin_params_->is_container_initiated) {
+    // Only same-origin navigations without cross-origin redirects can
+    // expose response details (status-code / mime-type).
+    // https://github.com/whatwg/fetch/issues/1602
+    // Note that this condition checks this navigation is not cross origin.
+    // Cross-origin redirects are checked as part of OnRequestRedirected().
+    commit_params_->navigation_timing->parent_resource_timing_access =
+        GetParentFrame()->GetLastCommittedOrigin().IsSameOriginWith(GetURL())
+            ? blink::mojom::ParentResourceTimingAccess::
+                  kReportWithResponseDetails
+            : blink::mojom::ParentResourceTimingAccess::
+                  kReportWithoutResponseDetails;
+  }
+
   navigation_or_document_handle_ =
       NavigationOrDocumentHandle::CreateForNavigation(*this);
 
@@ -3058,6 +3088,18 @@
   const bool is_same_origin_redirect =
       url::Origin::Create(common_params_->url)
           .IsSameOriginWith(redirect_info.new_url);
+
+  // Only same-origin navigations without cross-origin redirects can
+  // expose response details (status-code / mime-type).
+  // https://github.com/whatwg/fetch/issues/1602
+  if (!is_same_origin_redirect &&
+      commit_params_->navigation_timing->parent_resource_timing_access ==
+          blink::mojom::ParentResourceTimingAccess::
+              kReportWithResponseDetails) {
+    commit_params_->navigation_timing->parent_resource_timing_access =
+        blink::mojom::ParentResourceTimingAccess::kReportWithoutResponseDetails;
+  }
+
   did_receive_early_hints_before_cross_origin_redirect_ |=
       did_create_early_hints_manager_params_ && !is_same_origin_redirect;
 
@@ -3780,6 +3822,12 @@
     return;
   }
 
+  // See https://github.com/whatwg/fetch/pull/1579
+  if (!response_head_->timing_allow_passed) {
+    commit_params_->navigation_timing->parent_resource_timing_access =
+        blink::mojom::ParentResourceTimingAccess::kDoNotReport;
+  }
+
   MaybeInjectIsolatedAppHeaders();
 
   {
@@ -4709,34 +4757,40 @@
     return;
   }
 
-  RenderFrameHostImpl* parent_rfh = GetParentFrame();
-
-  // Do not add ResourceTiming entries if the navigated URL does not have a
-  // parent.
-  if (!parent_rfh) {
-    return;
-  }
-
   // Some navigation are cancelled even before requesting and receiving a
   // response. Those cases are not supported and the ResourceTiming is not
   // reported to the parent.
-  if (!response_head_) {
+  if (!response()) {
     return;
   }
 
-  if (initiator_document_.AsRenderFrameHostIfValid() != parent_rfh) {
+  network::URLLoaderCompletionStatus status;
+  status.encoded_data_length = response()->encoded_data_length;
+  status.completion_time = base::TimeTicks::Now();
+  AddResourceTimingEntryForFailedSubframeNavigation(status);
+}
+
+void NavigationRequest::AddResourceTimingEntryForFailedSubframeNavigation(
+    const network::URLLoaderCompletionStatus& status) {
+  // For TAO-fail navigations, we would resort to fallback timing.
+  // See HTMLFrameOwnerElement::ReportFallbackResourceTimingIfNeeded().
+  DCHECK(response());
+  if (commit_params().navigation_timing->parent_resource_timing_access ==
+      blink::mojom::ParentResourceTimingAccess::kDoNotReport) {
     return;
   }
 
-  blink::mojom::ResourceTimingInfoPtr timing_info =
-      GenerateResourceTimingForNavigation(parent_rfh->GetLastCommittedOrigin(),
-                                          *common_params_, *commit_params_,
-                                          *response_head_);
-  timing_info->response_end = base::TimeTicks::Now();
-  parent_rfh->GetAssociatedLocalFrame()
-      ->AddResourceTimingEntryFromNonNavigatedFrame(
-          std::move(timing_info),
-          frame_tree_node()->frame_owner_element_type());
+  network::mojom::URLResponseHeadPtr response_head = response()->Clone();
+
+  bool allow_response_details =
+      commit_params().navigation_timing->parent_resource_timing_access ==
+      blink::mojom::ParentResourceTimingAccess::kReportWithResponseDetails;
+
+  GetParentFrame()->AddResourceTimingEntryForFailedSubframeNavigation(
+      frame_tree_node(), common_params().navigation_start,
+      commit_params().navigation_timing->redirect_end,
+      commit_params().original_url, common_params().url,
+      std::move(response_head), allow_response_details, status);
 }
 
 void NavigationRequest::OnRedirectChecksComplete(
@@ -4984,8 +5038,8 @@
       // fallback / resource timing are only reported if the navigation request
       // is logically still pending.
       ObjectNavigationFallbackBodyLoader::CreateAndStart(
-          *this, *common_params_, *commit_params_, *response(),
-          std::move(response_body_), std::move(url_loader_client_endpoints_),
+          *this, std::move(response_body_),
+          std::move(url_loader_client_endpoints_),
           base::BindOnce(&NavigationRequest::OnRequestFailedInternal,
                          weak_factory_.GetWeakPtr(),
                          network::URLLoaderCompletionStatus(net::ERR_ABORTED),
@@ -5007,6 +5061,7 @@
       result.action() == NavigationThrottle::CANCEL ||
       !response_should_be_rendered_) {
     MaybeAddResourceTimingEntryForCancelledNavigation();
+
     // Reset the RenderFrameHost that had been computed for the commit of the
     // navigation.
     render_frame_host_ = nullptr;
@@ -5228,8 +5283,22 @@
   absl::optional<base::UnguessableToken> nonce =
       render_frame_host_->ComputeNonce(is_credentialless(),
                                        ComputeFencedFrameNonce());
+
+  // Determine if we should allow partitioned StorageKeys.
+  //
+  // If this is a main frame navigation then the value of
+  // third_party_storage_partitioning_enabled is irrelevant because main frames
+  // are always first-party by definition. If this is a subframe navigation
+  // then the main frame will have the correct value.
+  bool third_party_storage_partitioning_enabled = false;
+  if (!IsInMainFrame()) {
+    third_party_storage_partitioning_enabled =
+        GetRenderFrameHost()->IsMainFrameThirdPartyStoragePartitioningEnabled();
+  }
+
   commit_params_->storage_key = render_frame_host_->CalculateStorageKey(
-      GetOriginToCommit().value(), base::OptionalToPtr(nonce));
+      GetOriginToCommit().value(), base::OptionalToPtr(nonce),
+      third_party_storage_partitioning_enabled);
   commit_params_->session_storage_key =
       frame_tree_node()->frame_tree().GetSessionStorageKey(
           commit_params_->storage_key);
@@ -5443,13 +5512,6 @@
         commit_params_->pending_history_list_offset;
     page_restore_params->current_history_list_length =
         commit_params_->current_history_list_length;
-    page_restore_params->view_transition_state =
-        std::move(commit_params_->view_transition_state);
-    // Since we moved the view transition state to page restore params, we
-    // should reset the commit params one (move doesn't clear the optional).
-    // This ensures that we don't erroneously think that view_transition_state
-    // is unhandled and attempt to clean it up.
-    commit_params_->view_transition_state.reset();
     activated_entry = controller->GetBackForwardCache().RestoreEntry(
         nav_entry_id_, std::move(page_restore_params));
     // The only time activated_entry can be nullptr here, is if the
@@ -5492,6 +5554,11 @@
     if (!weak_self)
       return;
 
+    // Use std::exchange instead of move, so that we clear out the optional on
+    // the commit_params.
+    activated_entry->SetViewTransitionState(
+        std::exchange(commit_params_->view_transition_state, {}));
+
     // Move the BackForwardCacheImpl::Entry into RenderFrameHostManager, in
     // preparation for committing. This entry may be either restored from the
     // backforward cache.
@@ -5553,9 +5620,14 @@
     if (!weak_self)
       return;
 
+    DCHECK(stored_page);
+    // Use std::exchange instead of move, so that we clear out the optional on
+    // the commit_params.
+    stored_page->SetViewTransitionState(
+        std::exchange(commit_params_->view_transition_state, {}));
+
     // Move the StoredPage into RenderFrameHostManager, in
     // preparation for committing. This entry may be used for prerendering.
-    DCHECK(stored_page);
     frame_tree_node_->render_manager()->ActivatePrerender(
         std::move(stored_page));
   }
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 3d53578..14d3a6c 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -260,7 +260,8 @@
       blink::mojom::NavigationInitiatorActivationAndAdStatus
           initiator_activation_and_ad_status,
       bool is_pdf,
-      bool is_embedder_initiated_fenced_frame_navigation = false);
+      bool is_embedder_initiated_fenced_frame_navigation = false,
+      bool is_container_initiated = false);
 
   // Creates a request for a renderer-initiated navigation.
   static std::unique_ptr<NavigationRequest> CreateRendererInitiated(
@@ -568,6 +569,11 @@
   // a request was made.
   void MaybeAddResourceTimingEntryForCancelledNavigation();
 
+  // Adds a resource timing entry to the parent in case of cancelled navigations
+  // and failed <object> navigations.
+  void AddResourceTimingEntryForFailedSubframeNavigation(
+      const network::URLLoaderCompletionStatus& status);
+
   // Lazily initializes and returns the mojo::NavigationClient interface used
   // for commit.
   mojom::NavigationClient* GetCommitNavigationClient();
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index 0746732..1a133a80 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -32,6 +32,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/navigation/navigation_params.h"
 #include "third_party/blink/public/common/origin_trials/scoped_test_origin_trial_policy.h"
+#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_context.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 
 namespace content {
@@ -711,6 +712,139 @@
             child_document->storage_key());
 }
 
+// Test that the StorageKey's value is correctly affected by the
+// RuntimeFeatureStateContext.
+TEST_F(NavigationRequestTest, RuntimeFeatureStateStorageKey) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  // Because the StorageKey's (and Storage Partitioning's) usage of
+  // RuntimeFeatureState is only meant to disable partitioning (i.e.:
+  // first-party only), we need the make sure the net::features is always
+  // enabled.
+  scoped_feature_list.InitAndEnableFeature(
+      net::features::kThirdPartyStoragePartitioning);
+
+  // This lambda performs the navigation and compares the commit_params'
+  // StorageKey against the passed in one. If `disable_sp` is true then it will
+  // also disable ThirdPartyStoragePartitioningEnabled in the RFSC. It returns
+  // the new TestRenderFrameHost* to the navigated frame.
+  auto NavigateAndCompareKeys =
+      [](NavigationSimulator* navigation, const blink::StorageKey& key,
+         bool disable_sp = false) -> TestRenderFrameHost* {
+    navigation->Start();
+
+    NavigationRequest* request =
+        NavigationRequest::From(navigation->GetNavigationHandle());
+
+    if (disable_sp) {
+      request->GetMutableRuntimeFeatureStateContext()
+          .SetThirdPartyStoragePartitioningEnabled(false);
+    }
+
+    navigation->ReadyToCommit();
+
+    EXPECT_EQ(key, request->commit_params().storage_key);
+
+    navigation->Commit();
+    return static_cast<TestRenderFrameHost*>(
+        navigation->GetFinalRenderFrameHost());
+  };
+
+  // Throughout the test we'll be creating a frame tree with a main frame, a
+  // child frame, and a grandchild frame.
+  GURL main_url("https://main.com");
+  GURL b_url("https://b.com");
+  GURL c_url("https://c.com");
+
+  url::Origin main_origin = url::Origin::Create(main_url);
+  url::Origin b_origin = url::Origin::Create(b_url);
+  url::Origin c_origin = url::Origin::Create(c_url);
+
+  // Begin by testing with Storage Partitioning enabled.
+
+  auto main_navigation =
+      NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
+
+  // By definition the main frame's StorageKey will always be first party
+  blink::StorageKey main_frame_key =
+      blink::StorageKey::CreateFirstParty(main_origin);
+
+  NavigateAndCompareKeys(main_navigation.get(), main_frame_key);
+
+  TestRenderFrameHost* child_frame = static_cast<TestRenderFrameHost*>(
+      content::RenderFrameHostTester::For(main_rfh())->AppendChild("child"));
+
+  auto child_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(b_url, child_frame);
+
+  // The child and grandchild should both be third-party keys.
+  blink::StorageKey child_frame_key =
+      blink::StorageKey::Create(b_origin, net::SchemefulSite(main_origin),
+                                blink::mojom::AncestorChainBit::kCrossSite);
+
+  child_frame = NavigateAndCompareKeys(child_navigation.get(), child_frame_key);
+
+  TestRenderFrameHost* grandchild_frame =
+      child_frame->AppendChild("grandchild");
+
+  auto grandchild_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(c_url, grandchild_frame);
+
+  blink::StorageKey grandchild_frame_key =
+      blink::StorageKey::Create(c_origin, net::SchemefulSite(main_origin),
+                                blink::mojom::AncestorChainBit::kCrossSite);
+  grandchild_frame =
+      NavigateAndCompareKeys(grandchild_navigation.get(), grandchild_frame_key);
+
+  // Only the RuntimeFeatureStateContext in the main frame's matters. So
+  // disabling Storage Partitioning in the child_frame shouldn't affect the
+  // child's or the grandchild's StorageKey.
+  child_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(b_url, child_frame);
+
+  child_frame = NavigateAndCompareKeys(child_navigation.get(), child_frame_key,
+                                       /*disable_sp=*/true);
+
+  grandchild_frame = child_frame->AppendChild("grandchild");
+
+  grandchild_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(c_url, grandchild_frame);
+
+  grandchild_frame =
+      NavigateAndCompareKeys(grandchild_navigation.get(), grandchild_frame_key);
+
+  // Disabling Storage Partitioning on the main frame should cause the child's
+  // and grandchild's StorageKey to be first-party.
+  main_navigation =
+      NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
+
+  NavigateAndCompareKeys(main_navigation.get(), main_frame_key,
+                         /*disable_sp=*/true);
+
+  child_frame = static_cast<TestRenderFrameHost*>(
+      content::RenderFrameHostTester::For(main_rfh())->AppendChild("child"));
+
+  child_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(b_url, child_frame);
+
+  // The child and grandchild should both be first-party keys.
+  blink::StorageKey child_frame_key_1p =
+      blink::StorageKey::CreateFirstParty(b_origin);
+
+  child_frame =
+      NavigateAndCompareKeys(child_navigation.get(), child_frame_key_1p);
+
+  grandchild_frame = child_frame->AppendChild("grandchild");
+
+  blink::StorageKey grandchild_frame_key_1p =
+      blink::StorageKey::CreateFirstParty(c_origin);
+
+  grandchild_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(c_url, grandchild_frame);
+
+  grandchild_frame = NavigateAndCompareKeys(grandchild_navigation.get(),
+                                            grandchild_frame_key_1p);
+}
+
 TEST_F(NavigationRequestTest,
        NavigationToCredentiallessDocumentNetworkIsolationInfo) {
   auto* child_frame = static_cast<TestRenderFrameHost*>(
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index fc5ddfc..d4868e7 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -872,7 +872,8 @@
     base::TimeTicks navigation_start_time,
     bool is_embedder_initiated_fenced_frame_navigation,
     bool is_unfenced_top_navigation,
-    bool force_new_browsing_instance) {
+    bool force_new_browsing_instance,
+    bool is_container_initiated) {
   // |method != "POST"| should imply absence of |post_body|.
   if (method != "POST" && post_body) {
     NOTREACHED();
@@ -917,7 +918,7 @@
       std::move(blob_url_loader_factory), is_form_submission, impression,
       initiator_activation_and_ad_status, navigation_start_time,
       is_embedder_initiated_fenced_frame_navigation, is_unfenced_top_navigation,
-      force_new_browsing_instance);
+      force_new_browsing_instance, is_container_initiated);
 }
 
 void Navigator::BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
diff --git a/content/browser/renderer_host/navigator.h b/content/browser/renderer_host/navigator.h
index e3bd62d..f344ec2 100644
--- a/content/browser/renderer_host/navigator.h
+++ b/content/browser/renderer_host/navigator.h
@@ -165,7 +165,8 @@
       base::TimeTicks navigation_start_time,
       bool is_embedder_initiated_fenced_frame_navigation = false,
       bool is_unfenced_top_navigation = false,
-      bool force_new_browsing_instance = false);
+      bool force_new_browsing_instance = false,
+      bool is_container_initiated = false);
 
   // Called after BeforeUnloadCompleted callback is invoked from the renderer.
   // If |frame_tree_node| has a NavigationRequest waiting for the renderer
diff --git a/content/browser/renderer_host/page_impl.cc b/content/browser/renderer_host/page_impl.cc
index 03180d3..a4b876f 100644
--- a/content/browser/renderer_host/page_impl.cc
+++ b/content/browser/renderer_host/page_impl.cc
@@ -171,7 +171,8 @@
 }
 
 void PageImpl::ActivateForPrerendering(
-    StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts) {
+    StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts,
+    absl::optional<blink::ViewTransitionState> view_transition_state) {
   base::OnceClosure did_activate_render_views =
       base::BindOnce(&PageImpl::DidActivateAllRenderViewsForPrerendering,
                      weak_factory_.GetWeakPtr());
@@ -179,7 +180,8 @@
   base::RepeatingClosure barrier = base::BarrierClosure(
       render_view_hosts.size(), std::move(did_activate_render_views));
   for (const auto& rvh : render_view_hosts) {
-    base::TimeTicks navigation_start_to_send;
+    auto params = blink::mojom::PrerenderPageActivationParams::New();
+
     // Only send navigation_start to the RenderViewHost for the main frame to
     // avoid sending the info cross-origin. Only this RenderViewHost needs the
     // info, as we expect the other RenderViewHosts are made for cross-origin
@@ -188,16 +190,16 @@
     // not yet committed. These RenderViews still need to know about activation
     // so their documents are created in the non-prerendered state once their
     // navigation is committed.
-    if (main_document_->GetRenderViewHost() == &*rvh)
-      navigation_start_to_send = *activation_start_time_for_prerendering_;
+    if (main_document_->GetRenderViewHost() == &*rvh) {
+      params->activation_start = *activation_start_time_for_prerendering_;
+      params->view_transition_state = std::move(view_transition_state);
+    }
 
-    auto params = blink::mojom::PrerenderPageActivationParams::New();
     params->was_user_activated =
         main_document_->frame_tree_node()
                 ->has_received_user_gesture_before_nav()
             ? blink::mojom::WasActivatedOption::kYes
             : blink::mojom::WasActivatedOption::kNo;
-    params->activation_start = navigation_start_to_send;
     rvh->ActivatePrerenderedPage(std::move(params), barrier);
   }
 
diff --git a/content/browser/renderer_host/page_impl.h b/content/browser/renderer_host/page_impl.h
index dd06963..c74269d 100644
--- a/content/browser/renderer_host/page_impl.h
+++ b/content/browser/renderer_host/page_impl.h
@@ -141,7 +141,8 @@
   // documents from prerendered to activated. Tells the corresponding
   // RenderFrameHostImpls that the renderer will be activating their documents.
   void ActivateForPrerendering(
-      StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts_to_activate);
+      StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts_to_activate,
+      absl::optional<blink::ViewTransitionState> view_transition_state);
 
   // Prerender2:
   // Dispatches load events that were deferred to be dispatched after
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 7108cb11..1be900f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -4057,9 +4057,20 @@
   return frame_tree_node_->GetFencedFrameNonce();
 }
 
+bool RenderFrameHostImpl::IsMainFrameThirdPartyStoragePartitioningEnabled() {
+  RuntimeFeatureStateDocumentData* rfs_document_data_for_storage_key =
+      RuntimeFeatureStateDocumentData::GetForCurrentDocument(GetMainFrame());
+
+  DCHECK(rfs_document_data_for_storage_key);
+
+  return rfs_document_data_for_storage_key->runtime_feature_read_context()
+      .IsThirdPartyStoragePartitioningEnabled();
+}
+
 blink::StorageKey RenderFrameHostImpl::CalculateStorageKey(
     const url::Origin& new_rfh_origin,
-    const base::UnguessableToken* nonce) {
+    const base::UnguessableToken* nonce,
+    bool is_third_party_storage_partitioning_allowed) {
   if (nonce) {
     // If the nonce isn't null, we can use the simpler form of the constructor.
     return blink::StorageKey::CreateWithNonce(new_rfh_origin, *nonce);
@@ -4120,7 +4131,8 @@
   }
 
   return blink::StorageKey::Create(new_rfh_origin, top_level_site,
-                                   ancestor_chain_bit);
+                                   ancestor_chain_bit,
+                                   is_third_party_storage_partitioning_allowed);
 }
 
 void RenderFrameHostImpl::SetOriginDependentStateOfNewFrame(
@@ -4150,11 +4162,11 @@
   if (creator_frame) {
     // If we're given a parent/opener frame, copy the
     // RuntimeFeatureStateReadContext.
-    RuntimeFeatureStateDocumentData* document_data =
+    RuntimeFeatureStateDocumentData* rfs_document_data_from_creator =
         RuntimeFeatureStateDocumentData::GetForCurrentDocument(creator_frame);
-    DCHECK(document_data);
+    DCHECK(rfs_document_data_from_creator);
     RuntimeFeatureStateDocumentData::CreateForCurrentDocument(
-        this, document_data->runtime_feature_read_context());
+        this, rfs_document_data_from_creator->runtime_feature_read_context());
   } else {
     // Otherwise create a RuntimeFeatureStateContext. We need to construct a
     // RuntimeFeatureStateContext because its constructor initializes default
@@ -4163,8 +4175,11 @@
         this, blink::RuntimeFeatureStateContext());
   }
 
+  // For the StorageKey, we want the main frame's
+  // RuntimeFeatureStateReadContext.
   SetStorageKey(CalculateStorageKey(
-      new_frame_origin, base::OptionalToPtr(isolation_info_.nonce())));
+      new_frame_origin, base::OptionalToPtr(isolation_info_.nonce()),
+      IsMainFrameThirdPartyStoragePartitioningEnabled()));
 
   // Apply private network request policy according to our new origin.
   if (GetContentClient()->browser()->ShouldAllowInsecurePrivateNetworkRequests(
@@ -8256,6 +8271,17 @@
     return;
   }
 
+  // Container-initiated navigations must come from the same process as the
+  // parent.
+  if (begin_params->is_container_initiated) {
+    if (!GetParent() ||
+        (initiator_process_id != GetParent()->GetProcess()->GetID())) {
+      mojo::ReportBadMessage(
+          "container initiated navigation from non-parent process");
+      return;
+    }
+  }
+
   // If the request is bearing Private State Tokens parameters:
   // - it must not be a main-frame navigation, and
   // - for certain Private State Tokens operations, the frame's parent needs the
@@ -9746,6 +9772,48 @@
          navigation_request->DidEncounterError());
 }
 
+void RenderFrameHostImpl::AddResourceTimingEntryForFailedSubframeNavigation(
+    FrameTreeNode* child_frame,
+    base::TimeTicks start_time,
+    base::TimeTicks redirect_time,
+    const GURL& initial_url,
+    const GURL& final_url,
+    network::mojom::URLResponseHeadPtr response_head,
+    bool allow_response_details,
+    const network::URLLoaderCompletionStatus& completion_status) {
+  uint32_t status_code = 0;
+  std::string mime_type;
+  std::string normalized_server_timing;
+
+  response_head->headers->GetNormalizedHeader("Server-Timing",
+                                              &normalized_server_timing);
+
+  if (allow_response_details) {
+    status_code = response_head->headers->response_code();
+    mime_type = response_head->mime_type;
+  }
+
+  // To avoid cross-origin leaks, make sure to only to pass here data that
+  // is OK when TAO-gated (as in, timing information only).
+
+  absl::optional<blink::FrameToken> child_token_in_parent =
+      child_frame->GetRenderFrameHostManager()
+          .GetFrameTokenForSiteInstanceGroup(GetSiteInstance()->group());
+
+  if (!child_token_in_parent) {
+    return;
+  }
+
+  GetAssociatedLocalFrame()->AddResourceTimingEntryForFailedSubframeNavigation(
+      child_token_in_parent.value(), initial_url, start_time, redirect_time,
+      response_head->request_start, response_head->response_start, status_code,
+      mime_type, response_head->load_timing, response_head->connection_info,
+      response_head->alpn_negotiated_protocol,
+      base::Contains(url::GetSecureSchemes(),
+                     url::Origin::Create(final_url).scheme()),
+      response_head->is_validated, normalized_server_timing, completion_status);
+}
+
 void RenderFrameHostImpl::HandleRendererDebugURL(const GURL& url) {
   DCHECK(blink::IsRendererDebugURL(url));
 
@@ -12400,7 +12468,8 @@
 
   url::Origin origin = GetLastCommittedOrigin();
   blink::StorageKey storage_key_to_commit = CalculateStorageKey(
-      origin, base::OptionalToPtr(provisional_storage_key.nonce()));
+      origin, base::OptionalToPtr(provisional_storage_key.nonce()),
+      IsMainFrameThirdPartyStoragePartitioningEnabled());
   SetStorageKey(storage_key_to_commit);
 
   coep_reporter_ = navigation_request->TakeCoepReporter();
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 36d5326..d31f4b5 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -185,6 +185,7 @@
 namespace blink {
 class AssociatedInterfaceRegistry;
 class DocumentPolicy;
+class RuntimeFeatureStateReadContext;
 struct FramePolicy;
 struct TransferableMessage;
 struct UntrustworthyContextMenuParams;
@@ -1435,6 +1436,16 @@
       const absl::optional<std::string>& error_page_content,
       const blink::DocumentToken& document_token);
 
+  void AddResourceTimingEntryForFailedSubframeNavigation(
+      FrameTreeNode* child_frame,
+      base::TimeTicks start_time,
+      base::TimeTicks redirect_time,
+      const GURL& initial_url,
+      const GURL& final_url,
+      network::mojom::URLResponseHeadPtr response_head,
+      bool allow_response_details,
+      const network::URLLoaderCompletionStatus& completion_status);
+
   // Sends a renderer-debug URL to the renderer process for handling.
   void HandleRendererDebugURL(const GURL& url);
 
@@ -2654,12 +2665,19 @@
   // opaque origin instead).
   void SetOriginDependentStateOfNewFrame(RenderFrameHostImpl* creator_frame);
 
+  // Returns the value of `this`'s main frame's
+  // RuntimeFeatureStateReadContext::IsThirdPartyStoragePartitioningEnabled()
+  bool IsMainFrameThirdPartyStoragePartitioningEnabled();
+
   // Calculates the storage key for this RenderFrameHostImpl using the passed
-  // `new_rfh_origin`, and `nonce` and calculating the storage key's
-  // top_level_site` and `ancestor_bit` parameters. This takes into account
-  // possible host permissions of the top_level RenderFrameHostImpl.
-  blink::StorageKey CalculateStorageKey(const url::Origin& new_rfh_origin,
-                                        const base::UnguessableToken* nonce);
+  // `new_rfh_origin`, and `nonce`, and
+  // `is_third_party_storage_partitioning_allowed` and deriving the storage
+  // key's top_level_site` and `ancestor_bit` parameters. This takes into
+  // account possible host permissions of the top_level RenderFrameHostImpl.
+  blink::StorageKey CalculateStorageKey(
+      const url::Origin& new_rfh_origin,
+      const base::UnguessableToken* nonce,
+      bool is_third_party_storage_partitioning_allowed);
 
   // Returns the BrowsingContextState associated with this RenderFrameHostImpl.
   // See class comments in BrowsingContextState for a more detailed description.
diff --git a/content/browser/renderer_host/render_frame_host_impl_unittest.cc b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
index 3f2fcca..9c8a9563 100644
--- a/content/browser/renderer_host/render_frame_host_impl_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
@@ -28,6 +28,8 @@
 #include "services/network/public/mojom/cors_origin_pattern.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_context.h"
+#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_read_context.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
@@ -539,6 +541,9 @@
 }
 
 TEST_F(RenderFrameHostImplTest, CalculateStorageKey) {
+  bool partitioning_allowed =
+      blink::StorageKey::IsThirdPartyStoragePartitioningEnabled();
+
   // Register extension scheme for testing.
   url::ScopedSchemeRegistryForTests scoped_registry;
   url::AddStandardScheme("chrome-extension", url::SCHEME_WITH_HOST);
@@ -577,7 +582,8 @@
 
   EXPECT_EQ(expected_grandchild_no_permissions_storage_key,
             grandchild_frame->CalculateStorageKey(
-                grandchild_frame->GetLastCommittedOrigin(), nullptr));
+                grandchild_frame->GetLastCommittedOrigin(), nullptr,
+                partitioning_allowed));
 
   // Give extension host permissions to `grandchild_frame`. Since
   // `grandchild_frame` is not the root non-extension frame
@@ -597,7 +603,8 @@
 
   EXPECT_EQ(expected_grandchild_no_permissions_storage_key,
             grandchild_frame->CalculateStorageKey(
-                grandchild_frame->GetLastCommittedOrigin(), nullptr));
+                grandchild_frame->GetLastCommittedOrigin(), nullptr,
+                partitioning_allowed));
 
   // Now give extension host permissions to `child_frame`. Since the root
   // extension rfh has host permissions to`child_frame` calling
@@ -622,9 +629,10 @@
           child_frame->GetLastCommittedOrigin(),
           net::SchemefulSite(child_frame->GetLastCommittedOrigin()),
           blink::mojom::AncestorChainBit::kSameSite);
-  EXPECT_EQ(expected_child_with_permissions_storage_key,
-            child_frame->CalculateStorageKey(
-                child_frame->GetLastCommittedOrigin(), nullptr));
+  EXPECT_EQ(
+      expected_child_with_permissions_storage_key,
+      child_frame->CalculateStorageKey(child_frame->GetLastCommittedOrigin(),
+                                       nullptr, partitioning_allowed));
 
   blink::StorageKey expected_grandchild_with_permissions_storage_key =
       blink::StorageKey::Create(
@@ -633,11 +641,15 @@
           blink::mojom::AncestorChainBit::kCrossSite);
   EXPECT_EQ(expected_grandchild_with_permissions_storage_key,
             grandchild_frame->CalculateStorageKey(
-                grandchild_frame->GetLastCommittedOrigin(), nullptr));
+                grandchild_frame->GetLastCommittedOrigin(), nullptr,
+                partitioning_allowed));
 }
 
 TEST_F(RenderFrameHostImplTest,
        CalculateStorageKeyWhenPassedOriginIsNotCurrentFrame) {
+  bool partitioning_allowed =
+      blink::StorageKey::IsThirdPartyStoragePartitioningEnabled();
+
   // Register extension scheme for testing.
   url::ScopedSchemeRegistryForTests scoped_registry;
   url::AddStandardScheme("chrome-extension", url::SCHEME_WITH_HOST);
@@ -677,9 +689,10 @@
           child_frame->GetLastCommittedOrigin(),
           net::SchemefulSite(child_frame->GetLastCommittedOrigin()),
           blink::mojom::AncestorChainBit::kSameSite);
-  EXPECT_EQ(expected_child_with_permissions_storage_key,
-            child_frame->CalculateStorageKey(
-                child_frame->GetLastCommittedOrigin(), nullptr));
+  EXPECT_EQ(
+      expected_child_with_permissions_storage_key,
+      child_frame->CalculateStorageKey(child_frame->GetLastCommittedOrigin(),
+                                       nullptr, partitioning_allowed));
 
   // CalculateStorageKey is called with an origin that the top level document
   // does not have host permissions to. A cross-site storage key is expected and
@@ -693,7 +706,266 @@
           blink::mojom::AncestorChainBit::kCrossSite);
   EXPECT_EQ(expected_storage_key_no_permissions,
             child_frame->CalculateStorageKey(
-                url::Origin::Create(no_host_permissions_url), nullptr));
+                url::Origin::Create(no_host_permissions_url), nullptr,
+                partitioning_allowed));
+}
+
+// Test that the correct StorageKey is calculated when a RFH takes its document
+// properties from a navigation.
+// TODO(https://crbug.com/888079): Once we are able to compute the origin to
+// commit in the browser, `navigation_request->commit_params().storage_key`
+// will contain the correct origin and it won't be necessary to override it
+// with `param.origin` anymore. Meaning this test may be removed because we
+// already check that the NavigationRequest calculates the correct key.
+TEST_F(RenderFrameHostImplTest,
+       CalculateStorageKeyTakeNewDocumentPropertiesFromNavigation) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  // Because the StorageKey's (and Storage Partitioning's) usage of
+  // RuntimeFeatureState is only meant to disable partitioning (i.e.:
+  // first-party only), we need the make sure the net::features is always
+  // enabled.
+  scoped_feature_list.InitAndEnableFeature(
+      net::features::kThirdPartyStoragePartitioning);
+
+  // This lamdba performs the navigation and disables Storage Partitioning for
+  // the navigation if `disable_sp` is true. It returns the new
+  // TestRenderFrameHost* to the navigated frame.
+  auto NavigateFrame = [](NavigationSimulator* navigation,
+                          bool disable_sp = false) -> TestRenderFrameHost* {
+    navigation->Start();
+
+    if (disable_sp) {
+      NavigationRequest* request =
+          NavigationRequest::From(navigation->GetNavigationHandle());
+      request->GetMutableRuntimeFeatureStateContext()
+          .SetThirdPartyStoragePartitioningEnabled(false);
+    }
+
+    navigation->Commit();
+    return static_cast<TestRenderFrameHost*>(
+        navigation->GetFinalRenderFrameHost());
+  };
+
+  // Throughout the test we'll be creating a frame tree with a main frame, a
+  // child frame, and a grandchild frame.
+  GURL main_url("https://main.com");
+  GURL b_url("https://b.com");
+  GURL c_url("https://c.com");
+
+  url::Origin main_origin = url::Origin::Create(main_url);
+  url::Origin b_origin = url::Origin::Create(b_url);
+  url::Origin c_origin = url::Origin::Create(c_url);
+
+  // Begin by testing with Storage Partitioning enabled.
+
+  auto main_navigation =
+      NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
+
+  // By definition the main frame's StorageKey will always be first party
+  blink::StorageKey main_frame_key =
+      blink::StorageKey::CreateFirstParty(main_origin);
+
+  NavigateFrame(main_navigation.get());
+
+  EXPECT_EQ(main_frame_key, main_test_rfh()->storage_key());
+
+  TestRenderFrameHost* child_frame = static_cast<TestRenderFrameHost*>(
+      RenderFrameHostTester::For(main_rfh())->AppendChild("child"));
+
+  auto child_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(b_url, child_frame);
+
+  // The child and grandchild should both be third-party keys.
+  blink::StorageKey child_frame_key =
+      blink::StorageKey::Create(b_origin, net::SchemefulSite(main_origin),
+                                blink::mojom::AncestorChainBit::kCrossSite);
+
+  child_frame = NavigateFrame(child_navigation.get());
+
+  EXPECT_EQ(child_frame_key, child_frame->storage_key());
+
+  TestRenderFrameHost* grandchild_frame =
+      child_frame->AppendChild("grandchild");
+
+  auto grandchild_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(c_url, grandchild_frame);
+
+  blink::StorageKey grandchild_frame_key =
+      blink::StorageKey::Create(c_origin, net::SchemefulSite(main_origin),
+                                blink::mojom::AncestorChainBit::kCrossSite);
+  grandchild_frame = NavigateFrame(grandchild_navigation.get());
+
+  EXPECT_EQ(grandchild_frame_key, grandchild_frame->storage_key());
+
+  // Only the RuntimeFeatureStateContext in the main frame's matters. So
+  // disabling Storage Partitioning in the child_frame shouldn't affect the
+  // child's or the grandchild's StorageKey.
+  child_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(b_url, child_frame);
+
+  child_frame = NavigateFrame(child_navigation.get(),
+                              /*disable_sp=*/true);
+  EXPECT_EQ(child_frame_key, child_frame->storage_key());
+
+  grandchild_frame = child_frame->AppendChild("grandchild");
+
+  grandchild_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(c_url, grandchild_frame);
+
+  grandchild_frame = NavigateFrame(grandchild_navigation.get());
+
+  EXPECT_EQ(grandchild_frame_key, grandchild_frame->storage_key());
+
+  // Disabling Storage Partitioning on the main frame should cause the child's
+  // and grandchild's StorageKey to be first-party.
+  main_navigation =
+      NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
+
+  NavigateFrame(main_navigation.get(),
+                /*disable_sp=*/true);
+
+  child_frame = static_cast<TestRenderFrameHost*>(
+      RenderFrameHostTester::For(main_rfh())->AppendChild("child"));
+
+  child_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(b_url, child_frame);
+
+  // The child and grandchild should both be first-party keys.
+  blink::StorageKey child_frame_key_1p =
+      blink::StorageKey::CreateFirstParty(b_origin);
+
+  child_frame = NavigateFrame(child_navigation.get());
+
+  EXPECT_EQ(child_frame_key_1p, child_frame->storage_key());
+
+  grandchild_frame = child_frame->AppendChild("grandchild");
+
+  blink::StorageKey grandchild_frame_key_1p =
+      blink::StorageKey::CreateFirstParty(c_origin);
+
+  grandchild_navigation =
+      NavigationSimulatorImpl::CreateRendererInitiated(c_url, grandchild_frame);
+
+  grandchild_frame = NavigateFrame(grandchild_navigation.get());
+
+  EXPECT_EQ(grandchild_frame_key_1p, grandchild_frame->storage_key());
+}
+
+// Test that CalculateStorageKey creates a first-party or third-party key
+// depending on state of Storage Partitioning the main frame's
+// RuntimeFeatureStateReadContext for a new unnavigated frame.
+TEST_F(RenderFrameHostImplTest, CalculateStorageKeyOfUnnavigatedFrame) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  // Because Storage partitioning's usage of RuntimeFeatureState is only meant
+  // to disable (i.e.: 1p only) partitioning, we need the make sure the feature
+  // is on first.
+  scoped_feature_list.InitAndEnableFeature(
+      net::features::kThirdPartyStoragePartitioning);
+
+  // This test will create a main frame that has Storage Partitioning disabled
+  // via its RuntimeFeatureStateReadContext. It will have a navigated child
+  // frame's whose RFSRC will be the default (i.e.: Storage Partitioning
+  // enabled) and that child will then spawn an unnavigated grandchild whose
+  // StorageKey should still depend upon the main frame's RFSRC.
+
+  GURL url = GURL("https://a.com");
+  GURL child_url = GURL("https://b.com");
+
+  // Start by giving the main frame a SP disabled
+  // RuntimeFeatureStateReadContext.
+  auto navigation =
+      NavigationSimulator::CreateRendererInitiated(url, main_rfh());
+  navigation->Start();
+
+  NavigationRequest* request =
+      NavigationRequest::From(navigation->GetNavigationHandle());
+
+  request->GetMutableRuntimeFeatureStateContext()
+      .SetThirdPartyStoragePartitioningEnabled(false);
+
+  navigation->Commit();
+
+  EXPECT_FALSE(
+      RuntimeFeatureStateDocumentData::GetForCurrentDocument(main_rfh())
+          ->runtime_feature_read_context()
+          .IsThirdPartyStoragePartitioningEnabled());
+
+  // Create a child frame and navigate to `child_url`.
+  auto* child_frame = main_test_rfh()->AppendChild("child");
+  auto child_navigation =
+      NavigationSimulator::CreateRendererInitiated(child_url, child_frame);
+  child_navigation->Commit();
+  child_frame = static_cast<TestRenderFrameHost*>(
+      child_navigation->GetFinalRenderFrameHost());
+
+  // Create a grand child and check it's StorageKey.
+  auto* grandchild_frame = child_frame->AppendChild("grandchild");
+
+  // Since Storage Partitioning is disabled, the key should be first party.
+  blink::StorageKey grandchild_frame_key_1p =
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(child_url));
+  EXPECT_EQ(grandchild_frame_key_1p, grandchild_frame->storage_key());
+
+  // Now perform the same test, except the main frame also gets a default
+  // RuntimeFeatureStateReadContext. (I.e.: Storage Partitioning enabled).
+  NavigationSimulator::NavigateAndCommitFromDocument(url, main_rfh());
+
+  child_frame = main_test_rfh()->AppendChild("child");
+  child_navigation =
+      NavigationSimulator::CreateRendererInitiated(child_url, child_frame);
+  child_navigation->Commit();
+  child_frame = static_cast<TestRenderFrameHost*>(
+      child_navigation->GetFinalRenderFrameHost());
+
+  grandchild_frame = child_frame->AppendChild("grandchild");
+
+  blink::StorageKey grandchild_frame_key =
+      blink::StorageKey::Create(url::Origin::Create(child_url),
+                                net::SchemefulSite(url::Origin::Create(url)),
+                                blink::mojom::AncestorChainBit::kCrossSite);
+  EXPECT_EQ(grandchild_frame_key, grandchild_frame->storage_key());
+}
+
+TEST_F(RenderFrameHostImplTest,
+       NewFrameInheritsRuntimeFeatureStateReadContext) {
+  GURL url = GURL("https://a.com");
+  GURL child_url = GURL("https://b.com");
+
+  // Start by giving the main frame a non-default
+  // RuntimeFeatureStateReadContext.
+
+  auto navigation =
+      NavigationSimulator::CreateRendererInitiated(url, main_rfh());
+  navigation->Start();
+
+  NavigationRequest* request =
+      NavigationRequest::From(navigation->GetNavigationHandle());
+
+  request->GetMutableRuntimeFeatureStateContext().SetTestFeatureEnabled(true);
+
+  navigation->Commit();
+
+  EXPECT_TRUE(RuntimeFeatureStateDocumentData::GetForCurrentDocument(main_rfh())
+                  ->runtime_feature_read_context()
+                  .IsTestFeatureEnabled());
+
+  // Now add a child and check its RFSRC.
+  auto* child_frame = main_test_rfh()->AppendChild("child");
+  EXPECT_TRUE(
+      RuntimeFeatureStateDocumentData::GetForCurrentDocument(child_frame)
+          ->runtime_feature_read_context()
+          .IsTestFeatureEnabled());
+
+  // Navigating the child away should change the RFSRC.
+  auto child_navigation =
+      NavigationSimulator::CreateRendererInitiated(child_url, child_frame);
+  child_navigation->Commit();
+  child_frame = static_cast<TestRenderFrameHost*>(
+      child_navigation->GetFinalRenderFrameHost());
+  EXPECT_FALSE(
+      RuntimeFeatureStateDocumentData::GetForCurrentDocument(child_frame)
+          ->runtime_feature_read_context()
+          .IsTestFeatureEnabled());
 }
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index ddc0a834..465a009 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -3906,10 +3906,10 @@
       for (const auto& rvh : render_view_hosts_to_restore) {
         blink::mojom::PageRestoreParamsPtr page_restore_params =
             pending_stored_page->page_restore_params().Clone();
-        // We only send view_transition_state to the main RenderViewHost, so
-        // clear it for any other RenderViewHosts.
-        if (&*rvh != current_frame_host()->GetRenderViewHost()) {
-          page_restore_params->view_transition_state.reset();
+        // We only send view_transition_state to the main RenderViewHost.
+        if (&*rvh == current_frame_host()->GetRenderViewHost()) {
+          page_restore_params->view_transition_state =
+              pending_stored_page->TakeViewTransitionState();
         }
         rvh->LeaveBackForwardCache(std::move(page_restore_params));
       }
@@ -3917,7 +3917,8 @@
       DCHECK_EQ(prev_state,
                 RenderFrameHostImpl::LifecycleStateImpl::kPrerendering);
       current_frame_host()->GetPage().ActivateForPrerendering(
-          render_view_hosts_to_restore);
+          render_view_hosts_to_restore,
+          pending_stored_page->TakeViewTransitionState());
     }
   }
 
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index b9ddf05..824e7cc 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -703,7 +703,10 @@
       params->extra_headers, std::move(blob_url_loader_factory),
       std::move(params->source_location), params->user_gesture,
       params->is_form_submission, params->impression,
-      params->initiator_activation_and_ad_status, navigation_start_time);
+      params->initiator_activation_and_ad_status, navigation_start_time,
+      /*is_embedder_initiated_fenced_frame_navigation=*/false,
+      /*is_unfenced_top_navigation=*/false,
+      /*force_new_browsing_instance=*/false, params->is_container_initiated);
 }
 
 void RenderFrameProxyHost::UpdateViewportIntersection(
diff --git a/content/browser/renderer_host/stored_page.cc b/content/browser/renderer_host/stored_page.cc
index 8a4b95e..af94bed2 100644
--- a/content/browser/renderer_host/stored_page.cc
+++ b/content/browser/renderer_host/stored_page.cc
@@ -99,4 +99,15 @@
   return std::move(render_view_hosts_);
 }
 
+void StoredPage::SetViewTransitionState(
+    absl::optional<blink::ViewTransitionState> view_transition_state) {
+  DCHECK(!view_transition_state_);
+  view_transition_state_ = std::move(view_transition_state);
+}
+
+absl::optional<blink::ViewTransitionState>
+StoredPage::TakeViewTransitionState() {
+  return std::exchange(view_transition_state_, {});
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/stored_page.h b/content/browser/renderer_host/stored_page.h
index a1a8bba..1a704286 100644
--- a/content/browser/renderer_host/stored_page.h
+++ b/content/browser/renderer_host/stored_page.h
@@ -93,6 +93,10 @@
   RenderFrameProxyHostMap TakeProxyHosts();
   RenderViewHostImplSafeRefSet TakeRenderViewHosts();
 
+  void SetViewTransitionState(
+      absl::optional<blink::ViewTransitionState> view_transition_state);
+  absl::optional<blink::ViewTransitionState> TakeViewTransitionState();
+
  private:
   void ClearAllObservers();
 
@@ -124,6 +128,10 @@
   blink::mojom::PageRestoreParamsPtr page_restore_params_;
 
   raw_ptr<Delegate> delegate_ = nullptr;
+
+  // View transition state to use when the page is activated, either via BFCache
+  // activation or prerender activation.
+  absl::optional<blink::ViewTransitionState> view_transition_state_;
 };
 
 }  // namespace content
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index e8d9a9c4..15e819d1 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1538,7 +1538,8 @@
           base::TimeTicks() /* renderer_before_unload_end */,
           absl::nullopt /* web_bundle_token */,
           blink::mojom::NavigationInitiatorActivationAndAdStatus::
-              kDidNotStartWithTransientActivation);
+              kDidNotStartWithTransientActivation,
+          false /* is_container_initiated */);
 
   // Receiving the invalid IPC message should lead to renderer process
   // termination.
diff --git a/content/browser/service_worker/fake_service_worker.cc b/content/browser/service_worker/fake_service_worker.cc
index 4f93752..dbc079f 100644
--- a/content/browser/service_worker/fake_service_worker.cc
+++ b/content/browser/service_worker/fake_service_worker.cc
@@ -237,11 +237,11 @@
 }
 
 void FakeServiceWorker::AddKeepAlive() {
-  NOTIMPLEMENTED();
+  idle_delay_.reset();
 }
 
 void FakeServiceWorker::ClearKeepAlive() {
-  NOTIMPLEMENTED();
+  idle_delay_ = base::Seconds(30);
 }
 
 void FakeServiceWorker::AddMessageToConsole(
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
index 58dce62..f70c1b4 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
@@ -12,6 +12,7 @@
 #include "base/types/optional_util.h"
 #include "build/chromeos_buildflags.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
+#include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/navigation_request_info.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
@@ -230,6 +231,11 @@
     storage_key = GetStorageKeyFromWorkerHost(new_origin);
   }
   if (!storage_key.has_value()) {
+    // If we're in this case then we couldn't get the StorageKey from the RFH,
+    // which means we also can't get the storage partitioning status from
+    // RuntimeFeatureState(Read)Context. Using
+    // CreateFromOriginAndIsolationInfo() will create a key based on
+    // net::features::kThirdPartyStoragePartitioning state.
     storage_key = blink::StorageKey::CreateFromOriginAndIsolationInfo(
         new_origin, isolation_info_);
   }
@@ -362,7 +368,20 @@
   RenderFrameHostImpl* frame_host = frame_tree_node->current_frame_host();
   if (!frame_host)
     return absl::nullopt;
-  return frame_host->CalculateStorageKey(origin, nonce);
+
+  // Determine if we should allow partitioned StorageKeys.
+  //
+  // If this is a main frame navigation then the value of
+  // third_party_storage_partitioning_enabled is irrelevant because main frames
+  // are always first-party by definition. If this is a subframe navigation
+  // then the main frame will have the correct value.
+  bool third_party_storage_partitioning_enabled = false;
+  if (!frame_host->is_main_frame()) {
+    third_party_storage_partitioning_enabled =
+        frame_host->IsMainFrameThirdPartyStoragePartitioningEnabled();
+  }
+  return frame_host->CalculateStorageKey(
+      origin, nonce, third_party_storage_partitioning_enabled);
 }
 
 absl::optional<blink::StorageKey>
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 54ad5cd..2e88866 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -2231,8 +2231,9 @@
     return;
   }
 
-  // Requests have not finished before their expiration.
-  bool stop_for_timeout = false;
+  // Are there requests that have not finished before their expiration.
+  bool has_kill_on_timeout = false;
+  bool has_continue_on_timeout = false;
   // In case, `request_timeouts_` can be modified in the callbacks initiated
   // in `MaybeTimeoutRequest`, we keep its contents locally during the
   // following while loop.
@@ -2245,8 +2246,14 @@
       break;
     }
     if (MaybeTimeoutRequest(info)) {
-      stop_for_timeout =
-          stop_for_timeout || info.timeout_behavior == KILL_ON_TIMEOUT;
+      switch (info.timeout_behavior) {
+        case KILL_ON_TIMEOUT:
+          has_kill_on_timeout = true;
+          break;
+        case CONTINUE_ON_TIMEOUT:
+          has_continue_on_timeout = true;
+          break;
+      }
     }
     timeout_iter = request_timeouts.erase(timeout_iter);
   }
@@ -2256,14 +2263,24 @@
   // TODO(crbug.com/1363504): remove the following DCHECK when the cause
   // identified.
   DCHECK_EQ(request_timeouts_.size(), inflight_requests_.size());
-  if (stop_for_timeout && running_status() != EmbeddedWorkerStatus::STOPPING)
+
+  if (has_kill_on_timeout &&
+      running_status() != EmbeddedWorkerStatus::STOPPING) {
     embedded_worker_->Stop();
+  }
 
   // For the timeouts below, there are no callbacks to timeout so there is
   // nothing more to do if the worker is already stopping.
   if (running_status() == EmbeddedWorkerStatus::STOPPING)
     return;
 
+  // If an request is expired and there is no other requests, we ask event
+  // queue to check if idle timeout should be scheduled. Event queue may
+  // schedule idle timeout if there is no events at the time.
+  if (has_continue_on_timeout && !HasWorkInBrowser()) {
+    endpoint()->ClearKeepAlive();
+  }
+
   // Check ping status.
   ping_controller_.CheckPingStatus();
 }
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index ada9251b..15dbdac 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -959,6 +959,8 @@
 TEST_F(ServiceWorkerVersionTest, RequestNowTimeout) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
+  auto* service_worker =
+      helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get());
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
             StartServiceWorker(version_.get()));
@@ -975,6 +977,11 @@
   run_loop.Run();
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, status.value());
 
+  service_worker->FlushForTesting();
+  // Should try to set idle timeout if the last request has expired and is
+  // CONTINUE_ON_TIMEOUT.
+  EXPECT_TRUE(service_worker->idle_delay().has_value());
+
   EXPECT_FALSE(version_->FinishRequest(request_id, /*was_handled=*/true));
 
   // CONTINUE_ON_TIMEOUT timeouts don't stop the service worker.
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index db96c1f..0127fec 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -2962,7 +2962,6 @@
                                         },
                                     },
                                     target));
-    ;
   }
 
  private:
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 3769320..f9ae18dd 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -3151,7 +3151,21 @@
   RenderFrameHostImpl* frame_host = node->current_frame_host();
   if (!frame_host)
     return absl::nullopt;
-  return frame_host->CalculateStorageKey(origin, nonce);
+
+  // Determine if we should allow partitioned StorageKeys.
+  //
+  // If this is a main frame navigation then the value of
+  // third_party_storage_partitioning_enabled is irrelevant because main frames
+  // are always first-party by definition. If this is a subframe navigation
+  // then the main frame will have the correct value.
+  bool third_party_storage_partitioning_enabled = false;
+  if (!frame_host->is_main_frame()) {
+    third_party_storage_partitioning_enabled =
+        frame_host->IsMainFrameThirdPartyStoragePartitioningEnabled();
+  }
+
+  return frame_host->CalculateStorageKey(
+      origin, nonce, third_party_storage_partitioning_enabled);
 }
 
 StoragePartitionImpl::URLLoaderNetworkContext::URLLoaderNetworkContext(
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc
index fb0535a..20dc2c6 100644
--- a/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -48,24 +48,22 @@
 
 namespace {
 
-bool KeyEquals(const base::Value* value,
+bool KeyEquals(const base::Value::Dict& dict,
                const char* key_name,
                const char* expected) {
-  const base::Value* content =
-      value->FindKeyOfType(key_name, base::Value::Type::STRING);
+  const std::string* content = dict.FindString(key_name);
   if (!content)
     return false;
-  return content->GetString() == expected;
+  return *content == expected;
 }
 
-bool KeyNotEquals(const base::Value* value,
+bool KeyNotEquals(const base::Value::Dict& dict,
                   const char* key_name,
                   const char* expected) {
-  const base::Value* content =
-      value->FindKeyOfType(key_name, base::Value::Type::STRING);
+  const std::string* content = dict.FindString(key_name);
   if (!content)
     return false;
-  return content->GetString() != expected;
+  return *content != expected;
 }
 
 }  // namespace
@@ -431,20 +429,20 @@
   // Check that a number of important keys exist in the metadata dictionary.
   absl::optional<base::Value> trace_json = base::JSONReader::Read(last_data());
   ASSERT_TRUE(trace_json);
-  const base::Value* metadata_json =
-      trace_json->FindKeyOfType("metadata", base::Value::Type::DICT);
+  const base::Value::Dict* metadata_json =
+      trace_json->GetDict().FindDict("metadata");
   ASSERT_TRUE(metadata_json);
 
-  EXPECT_TRUE(KeyNotEquals(metadata_json, "cpu-brand", "__stripped__"));
-  EXPECT_TRUE(KeyNotEquals(metadata_json, "network-type", "__stripped__"));
-  EXPECT_TRUE(KeyNotEquals(metadata_json, "os-name", "__stripped__"));
-  EXPECT_TRUE(KeyNotEquals(metadata_json, "user-agent", "__stripped__"));
+  EXPECT_TRUE(KeyNotEquals(*metadata_json, "cpu-brand", "__stripped__"));
+  EXPECT_TRUE(KeyNotEquals(*metadata_json, "network-type", "__stripped__"));
+  EXPECT_TRUE(KeyNotEquals(*metadata_json, "os-name", "__stripped__"));
+  EXPECT_TRUE(KeyNotEquals(*metadata_json, "user-agent", "__stripped__"));
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  EXPECT_TRUE(KeyNotEquals(metadata_json, "hardware-class", "__stripped__"));
+  EXPECT_TRUE(KeyNotEquals(*metadata_json, "hardware-class", "__stripped__"));
 #endif
 
   // The following field is not whitelisted and is supposed to be stripped.
-  EXPECT_TRUE(KeyEquals(metadata_json, "v8-version", "__stripped__"));
+  EXPECT_TRUE(KeyEquals(*metadata_json, "v8-version", "__stripped__"));
 }
 
 IN_PROC_BROWSER_TEST_F(TracingControllerTest,
diff --git a/content/browser/webauth/authenticator_common_impl.cc b/content/browser/webauth/authenticator_common_impl.cc
index 2c35fb2..27a89db 100644
--- a/content/browser/webauth/authenticator_common_impl.cc
+++ b/content/browser/webauth/authenticator_common_impl.cc
@@ -1350,8 +1350,8 @@
 
 #if BUILDFLAG(IS_WIN)
       GetWebAuthenticationDelegate()->OperationSucceeded(
-          GetBrowserContext(), authenticator->GetType() ==
-                                   device::FidoAuthenticator::Type::kWinNative);
+          GetBrowserContext(),
+          authenticator->GetType() == device::AuthenticatorType::kWinNative);
 #endif
 
       absl::optional<device::FidoTransportProtocol> transport =
@@ -1580,7 +1580,7 @@
 #if BUILDFLAG(IS_WIN)
   GetWebAuthenticationDelegate()->OperationSucceeded(
       GetBrowserContext(),
-      authenticator->GetType() == device::FidoAuthenticator::Type::kWinNative);
+      authenticator->GetType() == device::AuthenticatorType::kWinNative);
 #endif
 
   // Show an account picker for discoverable credential requests (empty allow
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index d2f7102..4fbba74 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -17,6 +17,7 @@
 import "skia/public/mojom/skcolor.mojom";
 import "services/network/public/mojom/content_security_policy.mojom";
 import "services/network/public/mojom/url_loader.mojom";
+import "services/network/public/mojom/url_loader_completion_status.mojom";
 import "services/network/public/mojom/url_loader_factory.mojom";
 import "services/network/public/mojom/url_request.mojom";
 import "services/network/public/mojom/url_response_head.mojom";
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 668c7a0..68654d5 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1079,7 +1079,7 @@
 // https://crbug.com/1356224 .
 BASE_FEATURE(kSecurePaymentConfirmationRemoveRpField,
              "SecurePaymentConfirmationRemoveRpField",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Make sendBeacon throw for a Blob with a non simple type.
 BASE_FEATURE(kSendBeaconThrowForBlobWithNonSimpleType,
diff --git a/content/public/test/fake_local_frame.cc b/content/public/test/fake_local_frame.cc
index f8191976..f648ddf 100644
--- a/content/public/test/fake_local_frame.cc
+++ b/content/public/test/fake_local_frame.cc
@@ -9,7 +9,6 @@
 #include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
 #include "third_party/blink/public/mojom/frame/media_player_action.mojom.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom.h"
-#include "third_party/blink/public/mojom/timing/resource_timing.mojom.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "ui/base/mojom/attributed_string.mojom.h"
@@ -79,15 +78,6 @@
     const std::vector<blink::mojom::WebFeature>&) {}
 
 void FakeLocalFrame::RenderFallbackContent() {}
-
-void FakeLocalFrame::AddResourceTimingEntryFromNonNavigatedFrame(
-    blink::mojom::ResourceTimingInfoPtr timing,
-    blink::FrameOwnerElementType parent_frame_element_type) {}
-
-void FakeLocalFrame::RenderFallbackContentWithResourceTiming(
-    blink::mojom::ResourceTimingInfoPtr,
-    const std::string& server_timing_value) {}
-
 void FakeLocalFrame::BeforeUnload(bool is_reload,
                                   BeforeUnloadCallback callback) {
   base::TimeTicks now = base::TimeTicks::Now();
@@ -204,6 +194,23 @@
 void FakeLocalFrame::SnapshotDocumentForViewTransition(
     SnapshotDocumentForViewTransitionCallback callback) {}
 
+void FakeLocalFrame::AddResourceTimingEntryForFailedSubframeNavigation(
+    const ::blink::FrameToken& subframe_token,
+    const GURL& initial_url,
+    ::base::TimeTicks start_time,
+    ::base::TimeTicks redirect_time,
+    ::base::TimeTicks request_start,
+    ::base::TimeTicks response_start,
+    uint32_t response_code,
+    const std::string& mime_type,
+    const ::net::LoadTimingInfo& load_timing_info,
+    ::net::HttpResponseInfo::ConnectionInfo connection_info,
+    const std::string& alpn_negotiated_protocol,
+    bool is_secure_transport,
+    bool is_validated,
+    const std::string& normalized_server_timing,
+    const ::network::URLLoaderCompletionStatus& completion_status) {}
+
 void FakeLocalFrame::BindFrameHostReceiver(
     mojo::ScopedInterfaceEndpointHandle handle) {
   receiver_.Bind(mojo::PendingAssociatedReceiver<blink::mojom::LocalFrame>(
diff --git a/content/public/test/fake_local_frame.h b/content/public/test/fake_local_frame.h
index e732e63..8930e12 100644
--- a/content/public/test/fake_local_frame.h
+++ b/content/public/test/fake_local_frame.h
@@ -8,6 +8,8 @@
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "net/http/http_response_info.h"
+#include "services/network/public/mojom/load_timing_info.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/messaging/transferable_message.h"
@@ -65,12 +67,6 @@
   void ReportBlinkFeatureUsage(
       const std::vector<blink::mojom::WebFeature>&) override;
   void RenderFallbackContent() override;
-  void AddResourceTimingEntryFromNonNavigatedFrame(
-      blink::mojom::ResourceTimingInfoPtr timing,
-      blink::FrameOwnerElementType parent_frame_element_type) override;
-  void RenderFallbackContentWithResourceTiming(
-      blink::mojom::ResourceTimingInfoPtr,
-      const std::string& server_timing_value) override;
   void BeforeUnload(bool is_reload, BeforeUnloadCallback callback) override;
   void MediaPlayerActionAt(const gfx::Point& location,
                            blink::mojom::MediaPlayerActionPtr action) override;
@@ -150,6 +146,22 @@
                          blink::mojom::TraverseCancelledReason reason) override;
   void SnapshotDocumentForViewTransition(
       SnapshotDocumentForViewTransitionCallback callback) override;
+  void AddResourceTimingEntryForFailedSubframeNavigation(
+      const ::blink::FrameToken& subframe_token,
+      const GURL& initial_url,
+      base::TimeTicks start_time,
+      base::TimeTicks redirect_time,
+      base::TimeTicks request_start,
+      base::TimeTicks response_start,
+      uint32_t response_code,
+      const std::string& mime_type,
+      const net::LoadTimingInfo& load_timing_info,
+      net::HttpResponseInfo::ConnectionInfo connection_info,
+      const std::string& alpn_negotiated_protocol,
+      bool is_secure_transport,
+      bool is_validated,
+      const std::string& normalized_server_timing,
+      const ::network::URLLoaderCompletionStatus& completion_status) override;
 
  private:
   void BindFrameHostReceiver(mojo::ScopedInterfaceEndpointHandle handle);
diff --git a/content/public/test/fake_remote_frame.cc b/content/public/test/fake_remote_frame.cc
index 9492218..04d8a077 100644
--- a/content/public/test/fake_remote_frame.cc
+++ b/content/public/test/fake_remote_frame.cc
@@ -63,10 +63,6 @@
 
 void FakeRemoteFrame::RenderFallbackContent() {}
 
-void FakeRemoteFrame::RenderFallbackContentWithResourceTiming(
-    blink::mojom::ResourceTimingInfoPtr,
-    const std::string& server_timing_value) {}
-
 void FakeRemoteFrame::AddResourceTimingFromChild(
     blink::mojom::ResourceTimingInfoPtr timing) {}
 
diff --git a/content/public/test/fake_remote_frame.h b/content/public/test/fake_remote_frame.h
index e8174cd2..1521517b 100644
--- a/content/public/test/fake_remote_frame.h
+++ b/content/public/test/fake_remote_frame.h
@@ -64,9 +64,6 @@
       const base::UnguessableToken& embedding_token) override;
   void SetPageFocus(bool is_focused) override;
   void RenderFallbackContent() override;
-  void RenderFallbackContentWithResourceTiming(
-      blink::mojom::ResourceTimingInfoPtr,
-      const std::string& server_timing_value) override;
   void AddResourceTimingFromChild(
       blink::mojom::ResourceTimingInfoPtr timing) override;
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 0569890..24fced83 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -944,6 +944,8 @@
       browser_navigation_timings.fetch_start;
 
   renderer_navigation_timings.input_start = input_start;
+  renderer_navigation_timings.parent_resource_timing_access =
+      browser_navigation_timings.parent_resource_timing_access;
 
   return renderer_navigation_timings;
 }
@@ -5757,7 +5759,7 @@
               : nullptr,
           info->impression, renderer_before_unload_start,
           renderer_before_unload_end, web_bundle_token_params,
-          initiator_activation_and_ad_status);
+          initiator_activation_and_ad_status, info->is_container_initiated);
 
   mojo::PendingAssociatedRemote<mojom::NavigationClient>
       navigation_client_remote;
diff --git a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
index 99689183..35131f3 100644
--- a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
@@ -292,9 +292,11 @@
 interface BidderWorklet {
   // Loads the same-origin realtime bidding signals URL (if necessary), and
   // after the rest of the arguments for it have been provided via
-  // `bid_finalizer`, invokes the worklet's generateBid() method, returning the
-  // generated bid and associated data. Waits for the worklet script to be
-  // loaded first, if needed.
+  // a FinishGenerateBid() call on `bid_finalizer`, invokes the worklet's
+  // generateBid() method, returning the generated bid and associated data.
+  // Waits for the worklet script to be loaded first, if needed.
+  // FinishGenerateBid() *must* always be called, even if no parameters are
+  // provided as promises.
   //
   // Arguments:
   // `bidder_worklet_non_shared_params` values that can vary in the
diff --git a/content/test/content_unittests_bundle_data.filelist b/content/test/content_unittests_bundle_data.filelist
index 1d651e73..6943b774 100644
--- a/content/test/content_unittests_bundle_data.filelist
+++ b/content/test/content_unittests_bundle_data.filelist
@@ -65,6 +65,7 @@
 //content/test/data/attribution_reporting/interop/dedup_key.json
 //content/test/data/attribution_reporting/interop/default_config.json
 //content/test/data/attribution_reporting/interop/destination_limit.json
+//content/test/data/attribution_reporting/interop/event_level_report_time.json
 //content/test/data/attribution_reporting/interop/event_level_storage_limit.json
 //content/test/data/attribution_reporting/interop/event_level_trigger_filter_data.json
 //content/test/data/attribution_reporting/interop/event_level_trigger_priority.json
diff --git a/content/test/data/attribution_reporting/interop/event_level_report_time.json b/content/test/data/attribution_reporting/interop/event_level_report_time.json
new file mode 100644
index 0000000..0fa5658
--- /dev/null
+++ b/content/test/data/attribution_reporting/interop/event_level_report_time.json
@@ -0,0 +1,277 @@
+{
+  "description": "Different report time windows for event-level reports",
+  "input": {
+    "sources": [
+      {
+        "timestamp": "1643235573000",
+        "registration_request": {
+          "source_origin": "https://source.test",
+          "attribution_src_url": "https://reporter.test/register-source",
+          "source_type": "navigation"
+        },
+        "responses": [{
+          "url": "https://reporter.test/register-source",
+          "response": {
+            "Attribution-Reporting-Register-Source": {
+              "destination": "https://destination.test",
+              "source_event_id": "123",
+              "event_report_window": "96400"
+            }
+          }
+        }]
+      },
+      {
+        "timestamp": "1643235574000",
+        "registration_request": {
+          "source_origin": "https://source.test",
+          "attribution_src_url": "https://reporter2.test/register-source",
+          "source_type": "navigation"
+        },
+        "responses": [{
+          "url": "https://reporter2.test/register-source",
+          "response": {
+            "Attribution-Reporting-Register-Source": {
+              "destination": "https://destination.test",
+              "source_event_id": "456",
+              "event_report_window": "182800"
+            }
+          }
+        }]
+      },
+      {
+        "timestamp": "1643235575000",
+        "registration_request": {
+          "source_origin": "https://source.test",
+          "attribution_src_url": "https://reporter3.test/register-source",
+          "source_type": "navigation"
+        },
+        "responses": [{
+          "url": "https://reporter3.test/register-source",
+          "response": {
+            "Attribution-Reporting-Register-Source": {
+              "destination": "https://destination.test",
+              "source_event_id": "789",
+              "expiry": "864000"
+            }
+          }
+        }]
+      },
+      {
+        "timestamp": "1643235576000",
+        "registration_request": {
+          "source_origin": "https://source.test",
+          "attribution_src_url": "https://reporter.test/register-source",
+          "source_type": "event"
+        },
+        "responses": [{
+          "url": "https://reporter.test/register-source",
+          "response": {
+            "Attribution-Reporting-Register-Source": {
+              "destination": "https://destination2.test",
+              "source_event_id": "666",
+              "event_report_window": "182800"
+            }
+          }
+        }]
+      }
+    ],
+    "triggers": [
+      // Event source should report at event report window.
+      {
+        "timestamp": "1643235577000",
+        "registration_request": {
+          "attribution_src_url": "https://reporter.test/register-trigger",
+          "destination_origin": "https://destination2.test"
+        },
+        "responses": [{
+          "url": "https://reporter.test/register-trigger",
+          "response": {
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ]
+            }
+          }
+        }]
+      },
+      // Should report at event report window.
+      {
+        "timestamp": "1643331873000",
+        "registration_request": {
+          "attribution_src_url": "https://reporter.test/register-trigger",
+          "destination_origin": "https://destination.test"
+        },
+        "responses": [{
+          "url": "https://reporter.test/register-trigger",
+          "response": {
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "1"
+                }
+              ]
+            }
+          }
+        }]
+      },
+      // Should report at 2 days window.
+      {
+        "timestamp": "1643408374000",
+        "registration_request": {
+          "attribution_src_url": "https://reporter2.test/register-trigger",
+          "destination_origin": "https://destination.test"
+        },
+        "responses": [{
+          "url": "https://reporter2.test/register-trigger",
+          "response": {
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "2"
+                }
+              ]
+            }
+          }
+        }]
+      },
+      // Should report at event report window.
+      {
+        "timestamp": "1643408374001",
+        "registration_request": {
+          "attribution_src_url": "https://reporter2.test/register-trigger",
+          "destination_origin": "https://destination.test"
+        },
+        "responses": [{
+          "url": "https://reporter2.test/register-trigger",
+          "response": {
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "3"
+                }
+              ]
+            }
+          }
+        }]
+      },
+      // Should report at 7 days window.
+      {
+        "timestamp": "1643840375000",
+        "registration_request": {
+          "attribution_src_url": "https://reporter3.test/register-trigger",
+          "destination_origin": "https://destination.test"
+        },
+        "responses": [{
+          "url": "https://reporter3.test/register-trigger",
+          "response": {
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "4"
+                }
+              ]
+            }
+          }
+        }]
+      },
+      // Should report at expiry time window.
+      {
+        "timestamp": "1643840375001",
+        "registration_request": {
+          "attribution_src_url": "https://reporter3.test/register-trigger",
+          "destination_origin": "https://destination.test"
+        },
+        "responses": [{
+          "url": "https://reporter3.test/register-trigger",
+          "response": {
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "5"
+                }
+              ]
+            }
+          }
+        }]
+      }
+    ]
+  },
+  "output": {
+    "event_level_results": [
+      {
+        "payload": {
+          "attribution_destination": "https://destination.test",
+          "randomized_trigger_rate": 0.0024,
+          "scheduled_report_time": "1643335573",
+          "source_event_id": "123",
+          "source_type": "navigation",
+          "trigger_data": "1"
+        },
+        "report_url": "https://reporter.test/.well-known/attribution-reporting/report-event-attribution",
+        "report_time": "1643335573000"
+      },
+      {
+        "payload": {
+          "attribution_destination": "https://destination.test",
+          "randomized_trigger_rate": 0.0024,
+          "scheduled_report_time": "1643411974",
+          "source_event_id": "456",
+          "source_type": "navigation",
+          "trigger_data": "2"
+        },
+        "report_url": "https://reporter2.test/.well-known/attribution-reporting/report-event-attribution",
+        "report_time": "1643411974000"
+      },
+      {
+        "payload": {
+          "attribution_destination": "https://destination.test",
+          "randomized_trigger_rate": 0.0024,
+          "scheduled_report_time": "1643421974",
+          "source_event_id": "456",
+          "source_type": "navigation",
+          "trigger_data": "3"
+        },
+        "report_url": "https://reporter2.test/.well-known/attribution-reporting/report-event-attribution",
+        "report_time": "1643421974000"
+      },
+      {
+        "payload": {
+          "attribution_destination": "https://destination2.test",
+          "randomized_trigger_rate": 0.0000025,
+          "scheduled_report_time": "1643421976",
+          "source_event_id": "666",
+          "source_type": "event",
+          "trigger_data": "1"
+       },
+       "report_time": "1643421976000",
+       "report_url": "https://reporter.test/.well-known/attribution-reporting/report-event-attribution"
+      },
+      {
+        "payload": {
+          "attribution_destination": "https://destination.test",
+          "randomized_trigger_rate": 0.0024,
+          "scheduled_report_time": "1643843975",
+          "source_event_id": "789",
+          "source_type": "navigation",
+          "trigger_data": "4"
+        },
+        "report_url": "https://reporter3.test/.well-known/attribution-reporting/report-event-attribution",
+        "report_time": "1643843975000"
+      },
+      {
+        "payload": {
+          "attribution_destination": "https://destination.test",
+          "randomized_trigger_rate": 0.0024,
+          "scheduled_report_time": "1644103175",
+          "source_event_id": "789",
+          "source_type": "navigation",
+          "trigger_data": "5"
+       },
+       "report_time": "1644103175000",
+       "report_url": "https://reporter3.test/.well-known/attribution-reporting/report-event-attribution"
+      }
+    ]
+  }
+}
diff --git a/content/test/data/interest_group/component_auction_component_decision_argument_validator.js b/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
index 9c7c7fe..ff91a14 100644
--- a/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
+++ b/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
@@ -87,7 +87,7 @@
         JSON.stringify(auctionConfig.perBuyerTimeouts);
   }
 
-  if (auctionConfig.perBuyerCumulativeTimeouts[buyerOrigin] !== 201) {
+  if (auctionConfig.perBuyerCumulativeTimeouts[buyerOrigin] !== 20100) {
     throw 'Wrong perBuyerCumulativeTimeouts ' +
         JSON.stringify(auctionConfig.perBuyerCumulativeTimeouts);
   }
diff --git a/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js b/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
index 0dc46876..0b6bd90e 100644
--- a/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
+++ b/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
@@ -90,8 +90,8 @@
   const perBuyerCumulativeTimeoutsJson =
       JSON.stringify(auctionConfig.perBuyerCumulativeTimeouts);
   if (!perBuyerCumulativeTimeoutsJson.includes('a.test') ||
-      !perBuyerCumulativeTimeoutsJson.includes('111') ||
-      auctionConfig.perBuyerCumulativeTimeouts['*'] != 151) {
+      !perBuyerCumulativeTimeoutsJson.includes('11100') ||
+      auctionConfig.perBuyerCumulativeTimeouts['*'] != 15100) {
     throw 'Wrong perBuyerCumulativeTimeouts ' + perBuyerCumulativeTimeoutsJson;
   }
 
diff --git a/content/test/data/interest_group/decision_argument_validator.js b/content/test/data/interest_group/decision_argument_validator.js
index 72e405a..a3486813 100644
--- a/content/test/data/interest_group/decision_argument_validator.js
+++ b/content/test/data/interest_group/decision_argument_validator.js
@@ -96,9 +96,9 @@
         JSON.stringify(auctionConfig.perBuyerTimeouts);
   }
 
-  if (auctionConfig.perBuyerCumulativeTimeouts[buyerAOrigin] !== 130 ||
-      auctionConfig.perBuyerCumulativeTimeouts[buyerBOrigin] !== 140 ||
-      auctionConfig.perBuyerCumulativeTimeouts['*'] !== 160) {
+  if (auctionConfig.perBuyerCumulativeTimeouts[buyerAOrigin] !== 13000 ||
+      auctionConfig.perBuyerCumulativeTimeouts[buyerBOrigin] !== 14000 ||
+      auctionConfig.perBuyerCumulativeTimeouts['*'] !== 16000) {
     throw 'Wrong perBuyerCumulativeTimeouts ' +
         JSON.stringify(auctionConfig.perBuyerCumulativeTimeouts);
   }
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index e156c12..55e289e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -384,8 +384,11 @@
 crbug.com/1382332 [ display-server-wayland intel-0x9bc5 linux no-clang-coverage release renderer-skia-gl ] Pixel_MediaRecorderFromVideoElement [ RetryOnFailure ]
 
 # Flakes on Pixel 4, Pixel 6, Nexus 5X, and Samsung A13 due to video stream being offset vertically by 1 pixel.
-crbug.com/1410332 [ android ] Pixel_VideoStreamFromWebGLAlphaCanvas [ RetryOnFailure ]
+# finder:group-start crbug.com/1410332
+crbug.com/1410332 [ android ] Pixel_VideoStreamFrom2DAlphaCanvas [ RetryOnFailure ]
 crbug.com/1410332 [ android ] Pixel_VideoStreamFrom2DAlphaCanvas_DisableOOPRaster [ RetryOnFailure ]
+crbug.com/1410332 [ android ] Pixel_VideoStreamFromWebGLAlphaCanvas [ RetryOnFailure ]
+# finder:group-end
 
 # Fails on Linux AMD RX 5500 XT with RADV 20.0.8
 crbug.com/1418987 [ linux amd-0x7340 angle-vulkan ] Pixel_OffscreenCanvasAccelerated2DWorker [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index a63f61c2..f52d079 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -624,17 +624,20 @@
 crbug.com/angleproject/3686 [ android angle-opengles ] deqp/functional/gles3/multisample/fbo_max_samples.html [ Failure ]
 
 # finder:group-start crbug.com/1382796 failures happen in many tests, so can be classified as stale for one specific test
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] conformance/canvas/rapid-resizing.html [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] conformance/context/context-release-with-workers.html [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] conformance2/sync/sync-webgl-specific.html [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] deqp/data/gles3/shaders/declarations.html [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] deqp/functional/gles3/primitiverestart/00.html [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] deqp/functional/gles3/textureshadow/* [ RetryOnFailure ]
+# finder:disable-narrowing
+crbug.com/1382796 [ android android-pixel-4 ] conformance/canvas/rapid-resizing.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] conformance/context/context-release-with-workers.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] conformance2/sync/sync-webgl-specific.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] deqp/data/gles3/shaders/declarations.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] deqp/functional/gles3/primitiverestart/00.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] deqp/functional/gles3/shadermatrix/transpose.html [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] deqp/functional/gles3/textureshadow/* [ RetryOnFailure ]
 crbug.com/1382796 [ android android-pixel-4 ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] conformance/textures/video/* [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] conformance/textures/video/* [ RetryOnFailure ]
 crbug.com/1382796 [ android android-pixel-4 ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/1382796 [ android android-pixel-4 angle-opengles passthrough ] conformance2/textures/video/* [ RetryOnFailure ]
+crbug.com/1382796 [ android android-pixel-4 ] conformance2/textures/video/* [ RetryOnFailure ]
+# finder:enable-narrowing
 # finder:group-end
 
 crbug.com/1419019 [ android android-pixel-4 no-passthrough ] conformance2/transform_feedback/too-small-buffers.html [ RetryOnFailure ]
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index d7bf5ca..d4e0576 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -1323,7 +1323,8 @@
           base::TimeTicks() /* renderer_before_unload_end */,
           absl::nullopt /* web_bundle_token */,
           blink::mojom::NavigationInitiatorActivationAndAdStatus::
-              kDidNotStartWithTransientActivation);
+              kDidNotStartWithTransientActivation,
+          false /* is_container_initiated */);
   auto common_params = blink::CreateCommonNavigationParams();
   common_params->navigation_start = base::TimeTicks::Now();
   common_params->input_start = navigation_input_start_;
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index 0491d69..3cdfea02 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -419,7 +419,8 @@
           base::TimeTicks() /* renderer_before_unload_end */,
           absl::nullopt /* web_bundle_token */,
           blink::mojom::NavigationInitiatorActivationAndAdStatus::
-              kDidNotStartWithTransientActivation);
+              kDidNotStartWithTransientActivation,
+          false /* is_container_initiated */);
   auto common_params = blink::CreateCommonNavigationParams();
   common_params->url = url;
   common_params->initiator_origin = GetLastCommittedOrigin();
diff --git a/device/fido/cros/authenticator.cc b/device/fido/cros/authenticator.cc
index 80b9bb3..1fcb6e5d 100644
--- a/device/fido/cros/authenticator.cc
+++ b/device/fido/cros/authenticator.cc
@@ -36,8 +36,8 @@
 
 ChromeOSAuthenticator::~ChromeOSAuthenticator() {}
 
-FidoAuthenticator::Type ChromeOSAuthenticator::GetType() const {
-  return Type::kChromeOS;
+AuthenticatorType ChromeOSAuthenticator::GetType() const {
+  return AuthenticatorType::kChromeOS;
 }
 
 std::string ChromeOSAuthenticator::GetId() const {
diff --git a/device/fido/cros/authenticator.h b/device/fido/cros/authenticator.h
index 7a0689e92..4562a04 100644
--- a/device/fido/cros/authenticator.h
+++ b/device/fido/cros/authenticator.h
@@ -81,7 +81,7 @@
       const CtapGetAssertionOptions& options,
       GetPlatformCredentialInfoForRequestCallback callback) override;
   void Cancel() override;
-  Type GetType() const override;
+  AuthenticatorType GetType() const override;
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
 
diff --git a/device/fido/fido_authenticator.cc b/device/fido/fido_authenticator.cc
index da4e66b..3f19050 100644
--- a/device/fido/fido_authenticator.cc
+++ b/device/fido/fido_authenticator.cc
@@ -186,8 +186,8 @@
                           absl::nullopt);
 }
 
-FidoAuthenticator::Type FidoAuthenticator::GetType() const {
-  return Type::kOther;
+AuthenticatorType FidoAuthenticator::GetType() const {
+  return AuthenticatorType::kOther;
 }
 
 std::string FidoAuthenticator::GetDisplayName() const {
diff --git a/device/fido/fido_authenticator.h b/device/fido/fido_authenticator.h
index 426f8370..efb4c0e 100644
--- a/device/fido/fido_authenticator.h
+++ b/device/fido/fido_authenticator.h
@@ -23,6 +23,7 @@
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_request_handler_base.h"
 #include "device/fido/fido_transport_protocol.h"
+#include "device/fido/fido_types.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/make_credential_request_handler.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -282,14 +283,8 @@
   virtual void Reset(ResetCallback callback);
   virtual void Cancel() = 0;
 
-  enum class Type {
-    kWinNative,  // i.e. webauthn.dll
-    kTouchID,    // the Chrome-native Touch ID integration on macOS
-    kChromeOS,   // the platform authenticator on Chrome OS
-    kOther,
-  };
   // GetType returns the type of the authenticator.
-  virtual Type GetType() const;
+  virtual AuthenticatorType GetType() const;
 
   // GetId returns a unique string representing this device. This string should
   // be distinct from all other devices concurrently discovered.
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index 78302aa6..3434598 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -374,7 +374,7 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  if (authenticator->GetType() == FidoAuthenticator::Type::kWinNative) {
+  if (authenticator->GetType() == AuthenticatorType::kWinNative) {
     DCHECK(transport_availability_info_.has_win_native_api_authenticator);
     transport_availability_info_.win_native_api_authenticator_id =
         authenticator->GetId();
diff --git a/device/fido/fido_types.h b/device/fido/fido_types.h
index 0987c5e..ddcd2ee 100644
--- a/device/fido/fido_types.h
+++ b/device/fido/fido_types.h
@@ -90,6 +90,15 @@
   kExtension,
 };
 
+// AuthenticatorType enumerates the different types of authenticators that this
+// code handles.
+enum class AuthenticatorType {
+  kWinNative,  // i.e. webauthn.dll
+  kTouchID,    // the Chrome-native Touch ID integration on macOS
+  kChromeOS,   // the platform authenticator on Chrome OS
+  kOther,
+};
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FIDO_TYPES_H_
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 0ddc8df..c337ace5 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -929,7 +929,7 @@
 
     EXPECT_EQ(handler->AuthenticatorsForTesting().size(), 1u);
     EXPECT_EQ(handler->AuthenticatorsForTesting().begin()->second->GetType() ==
-                  FidoAuthenticator::Type::kWinNative,
+                  AuthenticatorType::kWinNative,
               enable_api);
   }
 }
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index fd1d11c..989bd5a 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -647,7 +647,7 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  if (authenticator->GetType() == FidoAuthenticator::Type::kWinNative) {
+  if (authenticator->GetType() == AuthenticatorType::kWinNative) {
     state_ = State::kFinished;
     CancelActiveAuthenticators(authenticator->GetId());
     if (status != CtapDeviceResponseCode::kSuccess) {
diff --git a/device/fido/mac/authenticator.h b/device/fido/mac/authenticator.h
index 23cabb8..b1c56d79 100644
--- a/device/fido/mac/authenticator.h
+++ b/device/fido/mac/authenticator.h
@@ -61,7 +61,7 @@
       const CtapGetAssertionOptions& options,
       GetPlatformCredentialInfoForRequestCallback callback) override;
   void Cancel() override;
-  Type GetType() const override;
+  AuthenticatorType GetType() const override;
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
   absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
diff --git a/device/fido/mac/authenticator.mm b/device/fido/mac/authenticator.mm
index 48cf70b..98ba1695 100644
--- a/device/fido/mac/authenticator.mm
+++ b/device/fido/mac/authenticator.mm
@@ -129,8 +129,8 @@
   operation_.reset();
 }
 
-FidoAuthenticator::Type TouchIdAuthenticator::GetType() const {
-  return Type::kTouchID;
+AuthenticatorType TouchIdAuthenticator::GetType() const {
+  return AuthenticatorType::kTouchID;
 }
 
 std::string TouchIdAuthenticator::GetId() const {
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index e66e575..48a2c1f 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -748,7 +748,7 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  if (authenticator->GetType() == FidoAuthenticator::Type::kWinNative) {
+  if (authenticator->GetType() == AuthenticatorType::kWinNative) {
     state_ = State::kFinished;
     if (status != CtapDeviceResponseCode::kSuccess) {
       std::move(completion_callback_)
@@ -983,7 +983,7 @@
       request->resident_key_required =
 #if BUILDFLAG(IS_WIN)
           // Windows does not yet support rk=preferred.
-          authenticator->GetType() != FidoAuthenticator::Type::kWinNative &&
+          authenticator->GetType() != AuthenticatorType::kWinNative &&
 #endif
           auth_options.supports_resident_key &&
           !authenticator->DiscoverableCredentialStorageFull() &&
diff --git a/device/fido/win/authenticator.cc b/device/fido/win/authenticator.cc
index b9a255d9..874d71e 100644
--- a/device/fido/win/authenticator.cc
+++ b/device/fido/win/authenticator.cc
@@ -363,8 +363,8 @@
   win_api_->CancelCurrentOperation(&cancellation_id_);
 }
 
-FidoAuthenticator::Type WinWebAuthnApiAuthenticator::GetType() const {
-  return Type::kWinNative;
+AuthenticatorType WinWebAuthnApiAuthenticator::GetType() const {
+  return AuthenticatorType::kWinNative;
 }
 
 std::string WinWebAuthnApiAuthenticator::GetId() const {
diff --git a/device/fido/win/authenticator.h b/device/fido/win/authenticator.h
index 297c8b4..e4f9f11 100644
--- a/device/fido/win/authenticator.h
+++ b/device/fido/win/authenticator.h
@@ -90,7 +90,7 @@
       GetPlatformCredentialInfoForRequestCallback callback) override;
   void GetTouch(base::OnceClosure callback) override;
   void Cancel() override;
-  Type GetType() const override;
+  AuthenticatorType GetType() const override;
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
   absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
diff --git a/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc b/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc
index b7ac107..05334a5 100644
--- a/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc
+++ b/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc
@@ -132,9 +132,8 @@
     std::string rule_location;
 
     // If possible use the rule ID in the install warning.
-    if (auto* id_val =
-            rules_list[i].FindKeyOfType(kIDKey, base::Value::Type::INTEGER)) {
-      rule_location = base::StringPrintf("id %d", id_val->GetInt());
+    if (auto id = rules_list[i].GetDict().FindInt(kIDKey)) {
+      rule_location = base::StringPrintf("id %d", *id);
     } else {
       // Use one-based indices.
       rule_location = base::StringPrintf("index %zu", i + 1);
diff --git a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
index 3b97fbd..28afe8c 100644
--- a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
+++ b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
@@ -620,9 +620,9 @@
     if (it.second.is_int() && (include_empty || it.second.GetInt() > 0)) {
       result.insert(it.first);
     } else if (it.second.is_dict()) {
-      const base::Value* count = it.second.FindKeyOfType(
-          kExtensionItemCountPrefKey, base::Value::Type::INTEGER);
-      if (include_empty || (count && count->GetInt() > 0)) {
+      absl::optional<int> count =
+          it.second.GetDict().FindInt(kExtensionItemCountPrefKey);
+      if (include_empty || (count && *count > 0)) {
         result.insert(it.first);
       }
     }
diff --git a/extensions/common/manifest_handlers/action_handlers_handler.cc b/extensions/common/manifest_handlers/action_handlers_handler.cc
index 03dd7bb..85f6222 100644
--- a/extensions/common/manifest_handlers/action_handlers_handler.cc
+++ b/extensions/common/manifest_handlers/action_handlers_handler.cc
@@ -54,18 +54,18 @@
     std::string value;
     bool enabled_on_lock_screen = false;
     if (wrapped_value.is_dict()) {
-      const base::Value* action_value = wrapped_value.FindKeyOfType(
-          keys::kActionHandlerActionKey, base::Value::Type::STRING);
-      if (!action_value) {
+      const base::Value::Dict& wrapped_dict = wrapped_value.GetDict();
+      const std::string* action =
+          wrapped_dict.FindString(keys::kActionHandlerActionKey);
+      if (!action) {
         *error = errors::kInvalidActionHandlerDictionary;
         return false;
       }
-      value = action_value->GetString();
-      const base::Value* lock_screen_value = wrapped_value.FindKeyOfType(
-          keys::kActionHandlerEnabledOnLockScreenKey,
-          base::Value::Type::BOOLEAN);
-      if (lock_screen_value) {
-        enabled_on_lock_screen = lock_screen_value->GetBool();
+      value = *action;
+      absl::optional<bool> enabled =
+          wrapped_dict.FindBool(keys::kActionHandlerEnabledOnLockScreenKey);
+      if (enabled) {
+        enabled_on_lock_screen = *enabled;
       }
     } else if (wrapped_value.is_string()) {
       value = wrapped_value.GetString();
diff --git a/extensions/common/manifest_handlers/nacl_modules_handler.cc b/extensions/common/manifest_handlers/nacl_modules_handler.cc
index 1eb30487..ff4e754 100644
--- a/extensions/common/manifest_handlers/nacl_modules_handler.cc
+++ b/extensions/common/manifest_handlers/nacl_modules_handler.cc
@@ -54,23 +54,22 @@
 
   const base::Value::List& list = list_value->GetList();
   for (size_t i = 0; i < list.size(); ++i) {
-    if (!list[i].is_dict()) {
+    const base::Value::Dict* dict = list[i].GetIfDict();
+    if (!dict) {
       *error = errors::kInvalidNaClModules;
       return false;
     }
 
     // Get nacl_modules[i].path.
-    const base::Value* path_str = list[i].FindKeyOfType(
-        keys::kNaClModulesPath, base::Value::Type::STRING);
-    if (path_str == nullptr) {
+    const std::string* path = dict->FindString(keys::kNaClModulesPath);
+    if (path == nullptr) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidNaClModulesPath, base::NumberToString(i));
       return false;
     }
 
     // Get nacl_modules[i].mime_type.
-    const base::Value* mime_type = list[i].FindKeyOfType(
-        keys::kNaClModulesMIMEType, base::Value::Type::STRING);
+    const std::string* mime_type = dict->FindString(keys::kNaClModulesMIMEType);
     if (mime_type == nullptr) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidNaClModulesMIMEType, base::NumberToString(i));
@@ -79,8 +78,8 @@
 
     nacl_module_data->nacl_modules_.push_back(NaClModuleInfo());
     nacl_module_data->nacl_modules_.back().url =
-        extension->GetResourceURL(path_str->GetString());
-    nacl_module_data->nacl_modules_.back().mime_type = mime_type->GetString();
+        extension->GetResourceURL(*path);
+    nacl_module_data->nacl_modules_.back().mime_type = *mime_type;
   }
 
   extension->SetManifestData(keys::kNaClModules, std::move(nacl_module_data));
diff --git a/extensions/renderer/bindings/api_signature.cc b/extensions/renderer/bindings/api_signature.cc
index f89a3819..1cc05ad 100644
--- a/extensions/renderer/bindings/api_signature.cc
+++ b/extensions/renderer/bindings/api_signature.cc
@@ -33,39 +33,39 @@
 };
 
 std::vector<std::unique_ptr<ArgumentSpec>> ValueListToArgumentSpecs(
-    const base::Value& specification_list,
+    const base::Value::List& specification_list,
     bool uses_returns_async) {
-  std::vector<std::unique_ptr<ArgumentSpec>> signature;
-  auto size = specification_list.GetList().size();
+  std::vector<std::unique_ptr<ArgumentSpec>> signatures;
+  auto size = specification_list.size();
   // If the API specification uses the returns_async format we will be pushing a
   // callback onto the end of the argument spec list during the call to the ctor
   // later, so we make room for it now when we reserve the size.
   if (uses_returns_async)
     size++;
-  signature.reserve(size);
-  for (const auto& value : specification_list.GetList()) {
-    CHECK(value.is_dict());
-    signature.push_back(std::make_unique<ArgumentSpec>(value));
+  signatures.reserve(size);
+  for (const auto& signature : specification_list) {
+    CHECK(signature.is_dict());
+    signatures.push_back(std::make_unique<ArgumentSpec>(signature));
   }
 
-  return signature;
+  return signatures;
 }
 
 std::unique_ptr<APISignature::ReturnsAsync> BuildReturnsAsyncFromValues(
-    const base::Value& returns_async_spec,
+    const base::Value::Dict& returns_async_spec,
     bool api_supports_promises) {
   auto returns_async = std::make_unique<APISignature::ReturnsAsync>();
   if (api_supports_promises)
     returns_async->promise_support = binding::APIPromiseSupport::kSupported;
-  const base::Value* callback_optional =
-      returns_async_spec.FindKeyOfType("optional", base::Value::Type::BOOLEAN);
-  returns_async->optional = callback_optional && callback_optional->GetBool();
+  absl::optional<bool> callback_optional =
+      returns_async_spec.FindBool("optional");
+  returns_async->optional = callback_optional.value_or(false);
 
   // If response validation is enabled, parse the callback signature. Otherwise,
   // there's no reason to, so don't bother.
   if (binding::IsResponseValidationEnabled()) {
-    const base::Value* callback_params =
-        returns_async_spec.FindKeyOfType("parameters", base::Value::Type::LIST);
+    const base::Value::List* callback_params =
+        returns_async_spec.FindList("parameters");
     if (callback_params) {
       returns_async->signature =
           ValueListToArgumentSpecs(*callback_params, false);
@@ -558,7 +558,8 @@
     const std::string& api_name,
     bool is_event_signature) {
   bool uses_returns_async = returns_async != nullptr;
-  auto argument_specs = ValueListToArgumentSpecs(spec_list, uses_returns_async);
+  auto argument_specs =
+      ValueListToArgumentSpecs(spec_list.GetList(), uses_returns_async);
 
   // Asynchronous returns for an API are either defined in the returns_async
   // part of the specification or as a trailing function argument.
@@ -580,8 +581,8 @@
   std::unique_ptr<APISignature::ReturnsAsync> returns_async_struct;
   if (returns_async_spec) {
     bool api_supports_promises = returns_async != nullptr;
-    returns_async_struct =
-        BuildReturnsAsyncFromValues(*returns_async_spec, api_supports_promises);
+    returns_async_struct = BuildReturnsAsyncFromValues(
+        returns_async_spec->GetDict(), api_supports_promises);
   }
 
   return std::make_unique<APISignature>(std::move(argument_specs),
diff --git a/extensions/test/test_extension_dir.cc b/extensions/test/test_extension_dir.cc
index 0168c804..705d3eb 100644
--- a/extensions/test/test_extension_dir.cc
+++ b/extensions/test/test_extension_dir.cc
@@ -34,9 +34,7 @@
 void TestExtensionDir::WriteFile(const base::FilePath::StringType& filename,
                                  base::StringPiece contents) {
   base::ScopedAllowBlockingForTesting allow_blocking;
-  EXPECT_EQ(base::checked_cast<int>(contents.size()),
-            base::WriteFile(dir_.GetPath().Append(filename), contents.data(),
-                            contents.size()));
+  EXPECT_TRUE(base::WriteFile(dir_.GetPath().Append(filename), contents));
 }
 
 void TestExtensionDir::CopyFileTo(
diff --git a/fuchsia_web/webengine/renderer/web_engine_audio_renderer.cc b/fuchsia_web/webengine/renderer/web_engine_audio_renderer.cc
index d4fb641..828d1f09 100644
--- a/fuchsia_web/webengine/renderer/web_engine_audio_renderer.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_audio_renderer.cc
@@ -274,7 +274,7 @@
   if (GetPlaybackState() == PlaybackState::kStartPending)
     StartAudioConsumer();
 
-  ScheduleReadDemuxerStream();
+  ScheduleBufferTimers();
 }
 
 media::TimeSource* WebEngineAudioRenderer::GetTimeSource() {
@@ -294,7 +294,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   renderer_started_ = true;
-  ScheduleReadDemuxerStream();
+  ScheduleBufferTimers();
 }
 
 void WebEngineAudioRenderer::SetVolume(float volume) {
@@ -414,7 +414,7 @@
   }
 
   FlushInternal();
-  ScheduleReadDemuxerStream();
+  ScheduleBufferTimers();
 }
 
 base::TimeDelta WebEngineAudioRenderer::CurrentMediaTime() {
@@ -429,29 +429,27 @@
     const std::vector<base::TimeDelta>& media_timestamps,
     std::vector<base::TimeTicks>* wall_clock_times) {
   wall_clock_times->reserve(media_timestamps.size());
-  auto now = base::TimeTicks::Now();
 
   base::AutoLock lock(timeline_lock_);
 
   const bool is_time_moving = IsTimeMoving();
 
   if (media_timestamps.empty()) {
-    wall_clock_times->push_back(is_time_moving ? now : reference_time_);
+    wall_clock_times->push_back(is_time_moving ? base::TimeTicks::Now()
+                                               : reference_time_);
     return is_time_moving;
   }
 
-  base::TimeTicks wall_clock_base = is_time_moving ? reference_time_ : now;
+  base::TimeTicks wall_clock_base =
+      is_time_moving ? reference_time_ : base::TimeTicks::Now();
 
   for (base::TimeDelta timestamp : media_timestamps) {
-    base::TimeTicks wall_clock_time;
-
     auto relative_pos = timestamp - media_pos_;
     if (is_time_moving) {
       // See https://fuchsia.dev/reference/fidl/fuchsia.media#formulas .
       relative_pos = relative_pos * reference_delta_ / media_delta_;
     }
-    wall_clock_time = wall_clock_base + relative_pos;
-    wall_clock_times->push_back(wall_clock_time);
+    wall_clock_times->push_back(wall_clock_base + relative_pos);
   }
 
   return is_time_moving;
@@ -475,6 +473,7 @@
   stream_sink_.Unbind();
   sysmem_buffer_stream_.reset();
   read_timer_.Stop();
+  out_of_buffer_timer_.Stop();
   renderer_started_ = false;
 
   if (is_demuxer_read_pending_) {
@@ -504,7 +503,7 @@
     return;
   }
 
-  bool reschedule_read_timer = false;
+  bool reschedule_timers = false;
 
   if (status.has_presentation_timeline()) {
     if (GetPlaybackState() != PlaybackState::kStopped) {
@@ -519,7 +518,7 @@
       reference_delta_ = status.presentation_timeline().reference_delta;
       media_delta_ = status.presentation_timeline().subject_delta;
 
-      reschedule_read_timer = true;
+      reschedule_timers = true;
     }
   }
 
@@ -529,7 +528,7 @@
     DCHECK(!new_min_lead_time.is_zero());
     if (new_min_lead_time != min_lead_time_) {
       min_lead_time_ = new_min_lead_time;
-      reschedule_read_timer = true;
+      reschedule_timers = true;
     }
   }
   if (status.has_max_lead_time()) {
@@ -538,51 +537,82 @@
     DCHECK(!new_max_lead_time.is_zero());
     if (new_max_lead_time != max_lead_time_) {
       max_lead_time_ = new_max_lead_time;
-      reschedule_read_timer = true;
+      reschedule_timers = true;
     }
   }
 
-  if (reschedule_read_timer) {
-    read_timer_.Stop();
-    ScheduleReadDemuxerStream();
+  if (reschedule_timers) {
+    ScheduleBufferTimers();
   }
 
   RequestAudioConsumerStatus();
 }
 
-void WebEngineAudioRenderer::ScheduleReadDemuxerStream() {
+void WebEngineAudioRenderer::ScheduleBufferTimers() {
+  std::vector<base::TimeDelta> media_timestamps;
+  if (!last_packet_timestamp_.is_min()) {
+    media_timestamps.push_back(last_packet_timestamp_);
+  }
+  std::vector<base::TimeTicks> wall_clock_times;
+  bool is_time_moving = GetWallClockTimes(media_timestamps, &wall_clock_times);
+
+  ScheduleReadDemuxerStream(is_time_moving, wall_clock_times[0]);
+  ScheduleOutOfBufferTimer(is_time_moving, wall_clock_times[0]);
+}
+
+void WebEngineAudioRenderer::ScheduleReadDemuxerStream(
+    bool is_time_moving,
+    base::TimeTicks end_of_buffer_time) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  if (!renderer_started_ || !demuxer_stream_ || read_timer_.IsRunning() ||
-      is_demuxer_read_pending_ || is_at_end_of_stream_) {
+  read_timer_.Stop();
+
+  if (!renderer_started_ || !demuxer_stream_ || is_demuxer_read_pending_ ||
+      is_at_end_of_stream_) {
     return;
   }
 
-  base::TimeDelta next_read_delay;
-  if (!last_packet_timestamp_.is_min()) {
-    std::vector<base::TimeTicks> wall_clock_times;
-    bool is_time_moving =
-        GetWallClockTimes({last_packet_timestamp_}, &wall_clock_times);
-    base::TimeDelta relative_buffer_pos =
-        wall_clock_times[0] - base::TimeTicks::Now();
+  base::TimeTicks next_read_time;
 
+  // If playback is not active then there is no need to buffer more.
+  if (!is_time_moving) {
     // Check if we have buffered more than |max_lead_time_|.
-    if (relative_buffer_pos >= max_lead_time_) {
-      // If playback is not active then there is no need to buffer more.
-      if (!is_time_moving)
-        return;
-
-      // If the buffer is larger than |max_lead_time_|, then the next read
-      // should be delayed.
-      next_read_delay = relative_buffer_pos - max_lead_time_;
+    if (end_of_buffer_time >= base::TimeTicks::Now() + max_lead_time_) {
+      return;
     }
   }
 
-  read_timer_.Start(FROM_HERE, next_read_delay,
+  // Schedule the next read at the time when the buffer size will be below
+  // `max_lead_time_` (may be in the past).
+  next_read_time = end_of_buffer_time - max_lead_time_;
+
+  read_timer_.Start(FROM_HERE, next_read_time,
                     base::BindOnce(&WebEngineAudioRenderer::ReadDemuxerStream,
                                    base::Unretained(this)));
 }
 
+void WebEngineAudioRenderer::ScheduleOutOfBufferTimer(
+    bool is_time_moving,
+    base::TimeTicks end_of_buffer_time) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  out_of_buffer_timer_.Stop();
+
+  if (buffer_state_ == media::BUFFERING_HAVE_NOTHING || !is_time_moving ||
+      is_at_end_of_stream_) {
+    return;
+  }
+
+  // Time when the `stream_sink_` will run out of buffer.
+  base::TimeTicks out_of_buffer_time = end_of_buffer_time - min_lead_time_;
+
+  out_of_buffer_timer_.Start(
+      FROM_HERE, out_of_buffer_time,
+      base::BindOnce(&WebEngineAudioRenderer::SetBufferState,
+                     base::Unretained(this), media::BUFFERING_HAVE_NOTHING),
+      base::subtle::DelayPolicy::kFlexibleNoSooner);
+}
+
 void WebEngineAudioRenderer::ReadDemuxerStream() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(demuxer_stream_);
@@ -606,7 +636,7 @@
 
   if (drop_next_demuxer_read_result_) {
     drop_next_demuxer_read_result_ = false;
-    ScheduleReadDemuxerStream();
+    ScheduleBufferTimers();
     return;
   }
 
@@ -621,7 +651,7 @@
 
       // Continue reading the stream. Decryptor won't finish output buffer
       // initialization until it starts receiving data on the input.
-      ScheduleReadDemuxerStream();
+      ScheduleBufferTimers();
 
       client_->OnAudioConfigChange(demuxer_stream_->audio_decoder_config());
     } else {
@@ -659,7 +689,7 @@
 
   sysmem_buffer_stream_->EnqueueBuffer(std::move(buffer));
 
-  ScheduleReadDemuxerStream();
+  ScheduleBufferTimers();
 }
 
 void WebEngineAudioRenderer::SendInputPacket(
@@ -697,11 +727,14 @@
     GetWallClockTimes({packet->timestamp()}, &wall_clock_times);
     base::TimeDelta relative_buffer_pos =
         wall_clock_times[0] - base::TimeTicks::Now();
-    if (relative_buffer_pos >= min_lead_time_)
+    if (relative_buffer_pos >= min_lead_time_) {
       SetBufferState(media::BUFFERING_HAVE_ENOUGH);
-  }
 
-  ScheduleReadDemuxerStream();
+      // Reschedule timers to ensure that the state is changed back to
+      // `BUFFERING_HAVE_NOTHING` when necessary.
+      ScheduleBufferTimers();
+    }
+  }
 }
 
 void WebEngineAudioRenderer::SetBufferState(
@@ -723,6 +756,7 @@
   SetBufferState(media::BUFFERING_HAVE_NOTHING);
   last_packet_timestamp_ = base::TimeDelta::Min();
   read_timer_.Stop();
+  out_of_buffer_timer_.Stop();
   is_at_end_of_stream_ = false;
 
   if (is_demuxer_read_pending_) {
@@ -787,8 +821,6 @@
     // The packet will be sent after StreamSink is connected.
     delayed_packets_.push_back(std::move(packet));
   }
-
-  ScheduleReadDemuxerStream();
 }
 
 void WebEngineAudioRenderer::OnSysmemBufferStreamEndOfStream() {
diff --git a/fuchsia_web/webengine/renderer/web_engine_audio_renderer.h b/fuchsia_web/webengine/renderer/web_engine_audio_renderer.h
index 3f3f74a..3c0dc47 100644
--- a/fuchsia_web/webengine/renderer/web_engine_audio_renderer.h
+++ b/fuchsia_web/webengine/renderer/web_engine_audio_renderer.h
@@ -116,13 +116,23 @@
   void RequestAudioConsumerStatus();
   void OnAudioConsumerStatusChanged(fuchsia::media::AudioConsumerStatus status);
 
+  // Calls `ScheduleReadDemuxerStream()` and `ScheduleOutOfBufferTimer()` to
+  // schedule the corresponding timers.
+  void ScheduleBufferTimers();
+
   // Helpers to pump data from |demuxer_stream_| to |stream_sink_|.
-  void ScheduleReadDemuxerStream();
+  void ScheduleReadDemuxerStream(bool is_time_moving,
+                                 base::TimeTicks end_of_buffer_time);
   void ReadDemuxerStream();
   void OnDemuxerStreamReadDone(
       media::DemuxerStream::Status status,
       media::DemuxerStream::DecoderBufferVector buffers);
 
+  // Schedules `out_of_buffer_timer_` timer, which transitions the renderer to
+  // the `BUFFERING_HAVE_NOTHING` state.
+  void ScheduleOutOfBufferTimer(bool is_time_moving,
+                                base::TimeTicks end_of_buffer_time);
+
   // Sends the specified packet to |stream_sink_|.
   void SendInputPacket(media::StreamProcessorHelper::IoPacket packet);
 
@@ -190,7 +200,8 @@
   media::BufferingState buffer_state_ = media::BUFFERING_HAVE_NOTHING;
 
   base::TimeDelta last_packet_timestamp_ = base::TimeDelta::Min();
-  base::OneShotTimer read_timer_;
+  base::DeadlineTimer read_timer_;
+  base::DeadlineTimer out_of_buffer_timer_;
 
   media::SysmemAllocatorClient sysmem_allocator_{"WebEngineAudioRenderer"};
   std::unique_ptr<media::SysmemCollectionClient> input_buffer_collection_;
diff --git a/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc b/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc
index 6ea45f99..017513ea 100644
--- a/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc
@@ -15,6 +15,7 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "media/base/buffering_state.h"
 #include "media/base/cdm_context.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/renderer_client.h"
@@ -1152,3 +1153,29 @@
   EXPECT_GT(stream_sink_->received_packets()->size(), 0U);
   EXPECT_FALSE(stream_sink_->received_end_of_stream());
 }
+
+TEST_P(WebEngineAudioRendererTest, Buffering) {
+  ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
+
+  constexpr base::TimeDelta kStartPos = base::TimeDelta();
+  ASSERT_NO_FATAL_FAILURE(StartPlayback(kStartPos));
+
+  constexpr base::TimeDelta kTimeBeforeBuffering = base::Milliseconds(500);
+  FillDemuxerStream(kTimeBeforeBuffering);
+
+  // Buffering state should be set to BUFFERING_HAVE_ENOUGH while the renderer
+  // still has data to play.
+  task_environment_.FastForwardBy(kTimeBeforeBuffering - kMinLeadTime -
+                                  kPacketDuration);
+  EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_ENOUGH);
+
+  // Buffering state should be updated once the renderer runs out of data it can
+  // read from the demuxer.
+  task_environment_.FastForwardBy(kPacketDuration);
+  EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_NOTHING);
+
+  // Buffering state should be updated once more data is read from the demuxer.
+  FillDemuxerStream(kTimeBeforeBuffering + kMinLeadTime);
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_ENOUGH);
+}
diff --git a/fuchsia_web/webengine/test/context_provider_for_test.cc b/fuchsia_web/webengine/test/context_provider_for_test.cc
index cb3216e4..da2eb11b 100644
--- a/fuchsia_web/webengine/test/context_provider_for_test.cc
+++ b/fuchsia_web/webengine/test/context_provider_for_test.cc
@@ -75,15 +75,23 @@
 
 }  // namespace
 
-ContextProviderForTest::ContextProviderForTest(
-    const base::CommandLine& command_line)
-    : realm_root_(BuildRealm(command_line)) {
+// static
+ContextProviderForTest ContextProviderForTest::Create(
+    const base::CommandLine& command_line) {
+  auto realm_root = BuildRealm(command_line);
   ::fuchsia::web::ContextProviderPtr context_provider;
   zx_status_t status =
-      realm_root_.component().Connect(context_provider_.NewRequest());
+      realm_root.component().Connect(context_provider.NewRequest());
   ZX_CHECK(status == ZX_OK, status) << "Connect to ContextProvider";
+  return ContextProviderForTest(std::move(realm_root),
+                                std::move(context_provider));
 }
 
+ContextProviderForTest::ContextProviderForTest(
+    ContextProviderForTest&&) noexcept = default;
+ContextProviderForTest& ContextProviderForTest::operator=(
+    ContextProviderForTest&&) noexcept = default;
+
 ContextProviderForTest::~ContextProviderForTest() {
   // We're about to shut down the realm; unbind to unhook the error handler.
   context_provider_.Unbind();
@@ -93,10 +101,23 @@
   run_loop.Run();
 }
 
-ContextProviderForDebugTest::ContextProviderForDebugTest(
-    const base::CommandLine& command_line)
-    : context_provider_(command_line) {}
+ContextProviderForTest::ContextProviderForTest(
+    ::component_testing::RealmRoot realm_root,
+    ::fuchsia::web::ContextProviderPtr context_provider)
+    : realm_root_(std::move(realm_root)),
+      context_provider_(std::move(context_provider)) {}
 
+// static
+ContextProviderForDebugTest ContextProviderForDebugTest::Create(
+    const base::CommandLine& command_line) {
+  return ContextProviderForDebugTest(
+      ContextProviderForTest::Create(command_line));
+}
+
+ContextProviderForDebugTest::ContextProviderForDebugTest(
+    ContextProviderForDebugTest&&) noexcept = default;
+ContextProviderForDebugTest& ContextProviderForDebugTest::operator=(
+    ContextProviderForDebugTest&&) noexcept = default;
 ContextProviderForDebugTest::~ContextProviderForDebugTest() = default;
 
 void ContextProviderForDebugTest::ConnectToDebug(
@@ -105,3 +126,7 @@
       std::move(debug_request));
   ZX_CHECK(status == ZX_OK, status) << "Connect to Debug";
 }
+
+ContextProviderForDebugTest::ContextProviderForDebugTest(
+    ContextProviderForTest context_provider)
+    : context_provider_(std::move(context_provider)) {}
diff --git a/fuchsia_web/webengine/test/context_provider_for_test.h b/fuchsia_web/webengine/test/context_provider_for_test.h
index e6fbd862..eefb909 100644
--- a/fuchsia_web/webengine/test/context_provider_for_test.h
+++ b/fuchsia_web/webengine/test/context_provider_for_test.h
@@ -16,11 +16,10 @@
 // system log.
 class ContextProviderForTest {
  public:
-  explicit ContextProviderForTest(const base::CommandLine& command_line);
+  static ContextProviderForTest Create(const base::CommandLine& command_line);
 
-  ContextProviderForTest(const ContextProviderForTest&) = delete;
-  ContextProviderForTest& operator=(const ContextProviderForTest&) = delete;
-
+  ContextProviderForTest(ContextProviderForTest&&) noexcept;
+  ContextProviderForTest& operator=(ContextProviderForTest&&) noexcept;
   ~ContextProviderForTest();
 
   ::fuchsia::web::ContextProviderPtr& ptr() { return context_provider_; }
@@ -28,6 +27,9 @@
   ::component_testing::RealmRoot& realm_root() { return realm_root_; }
 
  private:
+  ContextProviderForTest(::component_testing::RealmRoot realm_root,
+                         ::fuchsia::web::ContextProviderPtr context_provider);
+
   ::component_testing::RealmRoot realm_root_;
   ::fuchsia::web::ContextProviderPtr context_provider_;
 };
@@ -36,8 +38,12 @@
 // WebEngine's fuchsia::web::Debug interface.
 class ContextProviderForDebugTest {
  public:
-  explicit ContextProviderForDebugTest(const base::CommandLine& command_line);
+  static ContextProviderForDebugTest Create(
+      const base::CommandLine& command_line);
 
+  ContextProviderForDebugTest(ContextProviderForDebugTest&&) noexcept;
+  ContextProviderForDebugTest& operator=(
+      ContextProviderForDebugTest&&) noexcept;
   ~ContextProviderForDebugTest();
 
   ::fuchsia::web::ContextProviderPtr& ptr() { return context_provider_.ptr(); }
@@ -47,6 +53,8 @@
       ::fidl::InterfaceRequest<::fuchsia::web::Debug> debug_request);
 
  private:
+  explicit ContextProviderForDebugTest(ContextProviderForTest context_provider);
+
   ContextProviderForTest context_provider_;
 };
 
diff --git a/fuchsia_web/webengine/web_engine_debug_integration_test.cc b/fuchsia_web/webengine/web_engine_debug_integration_test.cc
index bd1cce6..fcdc6c0 100644
--- a/fuchsia_web/webengine/web_engine_debug_integration_test.cc
+++ b/fuchsia_web/webengine/web_engine_debug_integration_test.cc
@@ -26,7 +26,10 @@
 
 class WebEngineDebugIntegrationTest : public testing::Test {
  public:
-  WebEngineDebugIntegrationTest() {
+  WebEngineDebugIntegrationTest()
+      : web_context_provider_(ContextProviderForDebugTest::Create(
+            base::CommandLine(base::CommandLine::NO_PROGRAM))),
+        dev_tools_listener_binding_(&dev_tools_listener_) {
     web_context_provider_.ptr().set_error_handler(
         [](zx_status_t status) { FAIL() << zx_status_get_string(status); });
   }
@@ -53,11 +56,9 @@
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
 
-  ContextProviderForDebugTest web_context_provider_{
-      base::CommandLine(base::CommandLine::NO_PROGRAM)};
+  ContextProviderForDebugTest web_context_provider_;
   TestDebugListener dev_tools_listener_;
-  fidl::Binding<fuchsia::web::DevToolsListener> dev_tools_listener_binding_{
-      &dev_tools_listener_};
+  fidl::Binding<fuchsia::web::DevToolsListener> dev_tools_listener_binding_;
   fuchsia::web::DebugSyncPtr debug_;
 
   base::OnceClosure on_url_fetch_complete_ack_;
diff --git a/fuchsia_web/webengine/web_engine_integration_logging_test.cc b/fuchsia_web/webengine/web_engine_integration_logging_test.cc
index 2a413073..1886b17 100644
--- a/fuchsia_web/webengine/web_engine_integration_logging_test.cc
+++ b/fuchsia_web/webengine/web_engine_integration_logging_test.cc
@@ -54,7 +54,8 @@
   }
 
   void StartWebEngine(base::CommandLine command_line) override {
-    context_provider_.emplace(command_line);
+    context_provider_.emplace(
+        ContextProviderForTest::Create(std::move(command_line)));
     context_provider_->ptr().set_error_handler(
         [](zx_status_t status) { FAIL() << zx_status_get_string(status); });
   }
diff --git a/fuchsia_web/webengine/web_engine_integration_test.cc b/fuchsia_web/webengine/web_engine_integration_test.cc
index 00047022..27e6201 100644
--- a/fuchsia_web/webengine/web_engine_integration_test.cc
+++ b/fuchsia_web/webengine/web_engine_integration_test.cc
@@ -60,7 +60,8 @@
   }
 
   void StartWebEngine(base::CommandLine command_line) override {
-    context_provider_.emplace(command_line);
+    context_provider_.emplace(
+        ContextProviderForTest::Create(std::move(command_line)));
     context_provider_->ptr().set_error_handler(
         [](zx_status_t status) { FAIL() << zx_status_get_string(status); });
   }
diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py
index 1dcd017f..85f3c35 100644
--- a/infra/config/PRESUBMIT.py
+++ b/infra/config/PRESUBMIT.py
@@ -110,7 +110,7 @@
 def CheckOutagesConfigOnCommit(input_api, output_api):
   outages_pyl = input_api.os_path.join(
       input_api.PresubmitLocalPath(), 'generated/outages.pyl')
-  with open(outages_pyl) as f:
+  with open(outages_pyl, encoding='utf-8') as f:
     outages_config = input_api.ast.literal_eval(f.read())
 
   if not outages_config:
diff --git a/infra/config/generated/builders/ci/android-fieldtrial-rel/properties.json b/infra/config/generated/builders/ci/android-fieldtrial-rel/properties.json
index 34441d24..8eb4bd4 100644
--- a/infra/config/generated/builders/ci/android-fieldtrial-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-fieldtrial-rel/properties.json
@@ -50,11 +50,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 250,
diff --git a/infra/config/generated/builders/ci/win-fieldtrial-rel/properties.json b/infra/config/generated/builders/ci/win-fieldtrial-rel/properties.json
index 533fb471..973c8a9 100644
--- a/infra/config/generated/builders/ci/win-fieldtrial-rel/properties.json
+++ b/infra/config/generated/builders/ci/win-fieldtrial-rel/properties.json
@@ -44,11 +44,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 80,
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index cd545ca3..23d1bf7 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -2244,7 +2244,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
+      dimensions: "free_space:high"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
@@ -3467,12 +3467,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -3557,12 +3551,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -3647,12 +3635,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 300,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -3737,12 +3719,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -3826,12 +3802,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 150,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -3914,11 +3884,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "GLOG_vmodule": "bridge*=2",'
@@ -4003,11 +3968,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "jobs": 150,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "GLOG_vmodule": "bridge*=2",'
@@ -4092,11 +4052,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "GLOG_vmodule": "bridge*=2",'
@@ -4180,11 +4135,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "GLOG_vmodule": "bridge*=2",'
@@ -4270,12 +4220,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -4359,12 +4303,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 300,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -4449,12 +4387,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 80,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -4538,12 +4470,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -4627,12 +4553,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "enable_ats": false,'
-        '    "jobs": 300,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -4715,11 +4635,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "jobs": 250,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -4808,11 +4723,6 @@
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "jobs": 150,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "bootstrap_env": {'
         '      "RBE_deps_cache_mode": "reproxy",'
@@ -28678,11 +28588,6 @@
         '      ]'
         '    }'
         '  },'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "instance": "rbe-chromium-untrusted",'
         '    "jobs": 300,'
@@ -28802,11 +28707,6 @@
         '  "$build/code_coverage": {'
         '    "use_clang_coverage": true'
         '  },'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "instance": "rbe-chromium-untrusted",'
         '    "jobs": 300,'
@@ -28926,11 +28826,6 @@
         '  "$build/code_coverage": {'
         '    "use_clang_coverage": true'
         '  },'
-        '  "$build/goma": {'
-        '    "enable_ats": true,'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org"'
-        '  },'
         '  "$build/reclient": {'
         '    "instance": "rbe-chromium-untrusted",'
         '    "jobs": 300,'
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index b8d1318..df3572d 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -335,6 +335,7 @@
     name = "Android x64 Builder All Targets (dbg)",
     branch_selector = branches.selector.ANDROID_BRANCHES,
     builder_spec = builder_config.copy_from("ci/Android x64 Builder (dbg)"),
+    free_space = builders.free_space.high,
     console_view_entry = consoles.console_view_entry(
         category = "builder|x86",
         short_name = "64-all",
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 8b958b6..90dc2bf0 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -5,7 +5,7 @@
 
 load("//lib/branches.star", "branches")
 load("//lib/builder_config.star", "builder_config")
-load("//lib/builders.star", "builders", "cpu", "goma", "os", "reclient", "xcode")
+load("//lib/builders.star", "builders", "cpu", "os", "reclient", "xcode")
 load("//lib/ci.star", "ci")
 load("//lib/consoles.star", "consoles")
 load("//lib/structs.star", "structs")
@@ -522,7 +522,6 @@
     console_view_entry = consoles.console_view_entry(
         category = "android",
     ),
-    goma_backend = goma.backend.RBE_PROD,
 )
 
 fyi_ios_builder(
@@ -1119,8 +1118,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 15 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1145,8 +1142,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 15 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1164,8 +1159,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 6 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1184,8 +1177,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "GLOG_vmodule": "bridge*=2",
@@ -1206,8 +1197,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "GLOG_vmodule": "bridge*=2",
@@ -1229,8 +1218,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "GLOG_vmodule": "bridge*=2",
@@ -1252,8 +1239,6 @@
         category = "win",
         short_name = "re",
     ),
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 80,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1275,8 +1260,6 @@
         short_name = "re",
     ),
     execution_timeout = 6 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1295,8 +1278,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1315,8 +1296,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 250,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1342,8 +1321,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 15 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = goma.jobs.J300,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1369,8 +1346,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 6 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 150,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1396,8 +1371,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 150,
     reclient_bootstrap_env = {
         "GLOG_vmodule": "bridge*=2",
         "RBE_ip_reset_min_delay": "-1s",
@@ -1425,9 +1398,7 @@
         short_name = "re",
     ),
     execution_timeout = 6 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
     goma_enable_ats = False,
-    goma_jobs = 300,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1454,8 +1425,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 300,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1481,8 +1450,6 @@
         short_name = "cmp",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
-    goma_jobs = 150,
     reclient_bootstrap_env = {
         "RBE_ip_reset_min_delay": "-1s",
         "RBE_experimental_goma_deps_cache": "true",
@@ -1535,7 +1502,6 @@
         short_name = "and",
     ),
     execution_timeout = 10 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
     reclient_instance = reclient.instance.DEFAULT_UNTRUSTED,
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
     service_account = "chromium-build-perf-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -1572,7 +1538,6 @@
         short_name = "lnx",
     ),
     execution_timeout = 6 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
     reclient_instance = reclient.instance.DEFAULT_UNTRUSTED,
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
     service_account = "chromium-build-perf-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -1610,7 +1575,6 @@
         short_name = "win",
     ),
     execution_timeout = 6 * time.hour,
-    goma_backend = goma.backend.RBE_PROD,
     reclient_instance = reclient.instance.DEFAULT_UNTRUSTED,
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
     service_account = "chromium-build-perf-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -2471,7 +2435,6 @@
         category = "win11",
     ),
     experimental = True,
-    goma_backend = None,
     reclient_instance = reclient.instance.DEFAULT_TRUSTED,
     reclient_jobs = reclient.jobs.DEFAULT,
 )
@@ -2519,7 +2482,6 @@
     console_view_entry = consoles.console_view_entry(
         category = "win",
     ),
-    goma_backend = goma.backend.RBE_PROD,
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
diff --git a/ios/chrome/app/post_restore_app_agent_unittest.mm b/ios/chrome/app/post_restore_app_agent_unittest.mm
index a9d3fb03..736c49e4 100644
--- a/ios/chrome/app/post_restore_app_agent_unittest.mm
+++ b/ios/chrome/app/post_restore_app_agent_unittest.mm
@@ -95,11 +95,6 @@
     StorePreRestoreIdentity(local_state_.Get(), accountInfo);
   }
 
-  void EnableFeatureVariationFullscreen() {
-    scoped_feature_list_.InitAndEnableFeature(
-        post_restore_signin::features::kIOSNewPostRestoreExperience);
-  }
-
   void EnableFeatureVariationAlert() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {base::test::FeatureRefAndParams(
@@ -124,18 +119,7 @@
   MockAppStateChange(InitStageFinal);
 
   ClearPreRestoreIdentity(local_state_.Get());
-  EnableFeatureVariationFullscreen();
-  MockAppStateChange(InitStageFinal);
-}
-
-TEST_F(PostRestoreAppAgentTest, registerPromoFullscreen) {
-  EXPECT_CALL(*promos_manager_.get(),
-              RegisterPromoForSingleDisplay(
-                  promos_manager::Promo::PostRestoreSignInFullscreen))
-      .Times(1);
-
-  EnableFeatureVariationFullscreen();
-  SetFakePreRestoreAccountInfo();
+  EnableFeatureVariationAlert();
   MockAppStateChange(InitStageFinal);
 }
 
@@ -151,7 +135,7 @@
 }
 
 TEST_F(PostRestoreAppAgentTest, registerPromoDisablesReauthPrompt) {
-  EnableFeatureVariationFullscreen();
+  EnableFeatureVariationAlert();
   SetFakePreRestoreAccountInfo();
   auth_service_->SetReauthPromptForSignInAndSync();
   EXPECT_TRUE(auth_service_->ShouldReauthPromptForSignInAndSync());
@@ -159,43 +143,18 @@
   EXPECT_FALSE(auth_service_->ShouldReauthPromptForSignInAndSync());
 }
 
-TEST_F(PostRestoreAppAgentTest, deregisterPromoFullscreen) {
-  EXPECT_CALL(*promos_manager_.get(), DeregisterPromo(_)).Times(1);
-  EXPECT_CALL(
-      *promos_manager_.get(),
-      DeregisterPromo(promos_manager::Promo::PostRestoreSignInFullscreen))
-      .Times(1);
-
-  EnableFeatureVariationAlert();
-  SetFakePreRestoreAccountInfo();
-  ClearPreRestoreIdentity(local_state_.Get());
-  MockAppStateChange(InitStageFinal);
-}
-
 TEST_F(PostRestoreAppAgentTest, deregisterPromoAlert) {
   EXPECT_CALL(*promos_manager_.get(), DeregisterPromo(_)).Times(1);
   EXPECT_CALL(*promos_manager_.get(),
               DeregisterPromo(promos_manager::Promo::PostRestoreSignInAlert))
       .Times(1);
 
-  EnableFeatureVariationFullscreen();
+  EnableFeatureVariationAlert();
   SetFakePreRestoreAccountInfo();
   ClearPreRestoreIdentity(local_state_.Get());
   MockAppStateChange(InitStageFinal);
 }
 
-TEST_F(PostRestoreAppAgentTest, featureVariationSwitchToFullscreen) {
-  EXPECT_CALL(*promos_manager_.get(),
-              RegisterPromoForSingleDisplay(
-                  promos_manager::Promo::PostRestoreSignInFullscreen))
-      .Times(1);
-
-  EnableFeatureVariationFullscreen();
-  SetFakePreRestoreAccountInfo();
-
-  MockAppStateChange(InitStageFinal);
-}
-
 TEST_F(PostRestoreAppAgentTest, featureVariationSwitchToAlert) {
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForSingleDisplay(
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 357ae42..53ccdabf 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2331,7 +2331,7 @@
       <message name="IDS_IOS_TOOLS_MENU_CELL_NEW_FEATURE_BADGE" desc="Title and accessibility label of the badge displayed on a tools menu item when it represents a new feature [Length: 20em]" meaning="A badge with this title is displayed informing the user that this feature that is new to them.">
         New
       </message>
-      <message name="IDS_IOS_NEW_LABEL_FEATURE_BADGE" desc="Character representing the word new displayed inside the badge to represent a new features [iOS only]">
+      <message name="IDS_IOS_NEW_LABEL_FEATURE_BADGE" desc="Character representing the word new displayed inside the badge to represent a new features [CHAR_LIMIT=1][iOS only]" meaning="A badge with a character representing the word new is displayed to inform users that a feature a new. [CHAR_LIMIT=1][iOS only]">
         N
       </message>
       <message name="IDS_IOS_TOOLS_MENU_DOWNLOADS" desc="The iOS menu item for opening the downloads folder [iOS only]" meaning="[Length: unlimited]">
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm
index 1f58c73..2daf0a9 100644
--- a/ios/chrome/browser/autofill/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -220,10 +220,13 @@
        // TODO(crbug.com/1355264): Remove once launched.
        features::kAutofillLabelAffixRemoval},
       // Disabled
-      // TODO(crbug.com/1311937): Remove once launched.
-      // This feature is part of the AutofillRefinedPhoneNumberTypes rollout. As
-      // it is not supported on iOS yet, it is disabled.
-      {features::kAutofillConsiderPhoneNumberSeparatorsValidLabels});
+      {// TODO(crbug.com/1311937): Remove once launched.
+       // This feature is part of the AutofillRefinedPhoneNumberTypes rollout.
+       // As it is not supported on iOS yet, it is disabled.
+       features::kAutofillConsiderPhoneNumberSeparatorsValidLabels,
+       // TODO(crbug.com/1317961): Remove once launched. This feature is
+       // disabled since it is not supported on iOS.
+       features::kAutofillAlwaysParsePlaceholders});
 }
 
 void FormStructureBrowserTest::SetUp() {
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index de57220..808b7fda 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -18,6 +18,7 @@
     "//base",
     "//components/autofill/core/common",
     "//components/autofill/ios/browser",
+    "//components/bookmarks/common",
     "//components/breadcrumbs/core:feature_flags",
     "//components/commerce/core:feature_list",
     "//components/dom_distiller/core",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 06b5cc8..fa03cda0 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -25,6 +25,7 @@
 #import "components/autofill/core/common/autofill_payments_features.h"
 #import "components/autofill/core/common/autofill_switches.h"
 #import "components/autofill/ios/browser/autofill_switches.h"
+#import "components/bookmarks/common/bookmark_features.h"
 #import "components/breadcrumbs/core/features.h"
 #import "components/commerce/core/commerce_feature_list.h"
 #import "components/commerce/core/flag_descriptions.h"
@@ -1392,6 +1393,15 @@
      flag_descriptions::kIndicateAccountStorageErrorInAccountCellDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kIndicateAccountStorageErrorInAccountCell)},
+    {"enable-bookmarks-account-storage",
+     flag_descriptions::kEnableBookmarksAccountStorageName,
+     flag_descriptions::kEnableBookmarksAccountStorageDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(bookmarks::kEnableBookmarksAccountStorage)},
+    {"web-feed-feedback-reroute",
+     flag_descriptions::kWebFeedFeedbackRerouteName,
+     flag_descriptions::kWebFeedFeedbackRerouteDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kWebFeedFeedbackReroute)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index aaa7f89..d09e4c8a 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -274,6 +274,11 @@
 const char kEnableAutofillAddressSavePromptDescription[] =
     "Enable the Autofill address save prompts.";
 
+const char kEnableBookmarksAccountStorageName[] =
+    "Enable Bookmarks Account Storage";
+const char kEnableBookmarksAccountStorageDescription[] =
+    "Enable bookmarks account storage and related UI features.";
+
 const char kEnableCBDSignOutName[] = "Enable Clear Browsing Data Sign-out";
 const char kEnableCBDSignOutDescription[] =
     "Offer signed-in user to sign-out from Clear Browsing Data settings.";
@@ -971,6 +976,12 @@
 const char kWalletServiceUseSandboxDescription[] =
     "Uses the sandbox service for Google Payments API calls.";
 
+const char kWebFeedFeedbackRerouteName[] =
+    "Send discover feed feedback to a updated destination";
+const char kWebFeedFeedbackRerouteDescription[] =
+    "Directs discover feed feedback to a new target for better handling of the"
+    "feedback reports.";
+
 const char kWebPageDefaultZoomFromDynamicTypeName[] =
     "Use dynamic type size for default text zoom level";
 const char kWebPageDefaultZoomFromDynamicTypeDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index fd1894c..64efe23 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -206,6 +206,11 @@
 extern const char kDetectMainThreadFreezeName[];
 extern const char kDetectMainThreadFreezeDescription[];
 
+// Title and description for the flag to enable the bookmarks account storage
+// and related UI features.
+extern const char kEnableBookmarksAccountStorageName[];
+extern const char kEnableBookmarksAccountStorageDescription[];
+
 // Title and description for the flag to enable checking feed visibility on
 // attention log start.
 extern const char kEnableCheckVisibilityOnAttentionLogStartName[];
@@ -851,6 +856,11 @@
 extern const char kWalletServiceUseSandboxName[];
 extern const char kWalletServiceUseSandboxDescription[];
 
+// Title and description for the flag to control whether to send discover
+// feedback to a new product destination
+extern const char kWebFeedFeedbackRerouteName[];
+extern const char kWebFeedFeedbackRerouteDescription[];
+
 // Title and description for the flag to tie the default text zoom level to
 // the dynamic type setting.
 extern const char kWebPageDefaultZoomFromDynamicTypeName[];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 8ab8118..c3a3e797 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -204,7 +204,7 @@
 
 #pragma mark - ContentSuggestionsViewControllerAudience
 
-- (void)viewDidDisappear {
+- (void)viewWillDisappear {
   // Start no longer showing
   self.contentSuggestionsMediator.showingStartSurface = NO;
   DiscoverFeedServiceFactory::GetForBrowserState(
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 1676f64f..28733e6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -351,10 +351,10 @@
   }
 }
 
-- (void)viewDidDisappear:(BOOL)animated {
-  [super viewDidDisappear:animated];
+- (void)viewWillDisappear:(BOOL)animated {
+  [super viewWillDisappear:animated];
   if (ShouldShowReturnToMostRecentTabForStartSurface()) {
-    [self.audience viewDidDisappear];
+    [self.audience viewWillDisappear];
   }
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
index 1464d685..8931ff5 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
@@ -8,8 +8,8 @@
 // Audience for the ContentSuggestions, getting informations from it.
 @protocol ContentSuggestionsViewControllerAudience
 
-// Notifies the audience of the UIKit viewDidDisappear: callback.
-- (void)viewDidDisappear;
+// Notifies the audience of the UIKit viewWillDisappear: callback.
+- (void)viewWillDisappear;
 
 // Notifies the audience that the Return to Recent Tab tile has been added.
 - (void)returnToRecentTabWasAdded;
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index eaa4102..ab3b414c 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -536,6 +536,54 @@
   }
 }
 
+// Tests that the pull to refresh (iphone) or the refresh button (ipad) lands
+// the user on the top of the NTP even with a previously saved scroll position.
+- (void)testReload {
+  // Scroll to have a position to restored.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
+      performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
+
+  // Save the position before navigating.
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
+  CGFloat previousPosition = collectionView.contentOffset.y;
+
+  // Navigate and come back.
+  self.testServer->RegisterRequestHandler(
+      base::BindRepeating(&StandardResponse));
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+  const GURL pageURL = self.testServer->GetURL(kPageURL);
+
+  [ChromeEarlGrey loadURL:pageURL];
+  [ChromeEarlGrey waitForWebStateContainingText:kPageLoadedString];
+  [ChromeEarlGrey goBack];
+
+  // Check that the new position is the same.
+  GREYAssertEqual(previousPosition, collectionView.contentOffset.y,
+                  @"NTP is not at the same position.");
+
+  if ([ChromeEarlGrey isIPadIdiom]) {
+    // Have to scroll up to the top since tapping on reload button does not
+    // automatically scroll to the top when feed is off or if feed returns no
+    // contents (e.g. upstream bots). TODO(crbug.com/1406940): Look into why the
+    // Feed only scrolls up when there is content.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
+        performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
+    // Tap on reload button.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::ReloadButton()]
+        performAction:grey_tap()];
+  } else {
+    // Get back to the top of the page and then pull down to trigger Pull To
+    // Refresh
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
+        performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
+        performAction:grey_swipeSlowInDirection(kGREYDirectionDown)];
+  }
+  [ChromeEarlGreyUI waitForAppToIdle];
+  [self
+      testNTPInitialPositionAndContent:[NewTabPageAppInterface collectionView]];
+}
+
 // Tests that the position of the collection view is restored when navigating
 // back to the NTP.
 - (void)testPositionRestored {
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index 7b94e2b0..76eb62a8 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -83,7 +83,7 @@
     "//ios/chrome/browser/ui/first_run/default_browser",
     "//ios/chrome/browser/ui/first_run/signin",
     "//ios/chrome/browser/ui/first_run/tangible_sync",
-    "//ios/chrome/browser/ui/first_run/welcome",
+    "//ios/chrome/browser/ui/first_run/tos",
     "//ios/chrome/browser/ui/screen:screen_provider",
     "//ios/chrome/browser/ui/screen:screen_type",
     "//ios/chrome/browser/ui/settings/resources:enterprise_icon",
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/Contents.json b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/Contents.json
deleted file mode 100644
index bb15c9bd..0000000
--- a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "welcome_screen_banner_light@2x.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "filename" : "welcome_screen_banner_dark@2x.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "welcome_screen_banner_light@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "filename" : "welcome_screen_banner_dark@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png
deleted file mode 100644
index 1d18224..0000000
--- a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png
deleted file mode 100644
index 46d7045..0000000
--- a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png
deleted file mode 100644
index f33edf85..0000000
--- a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png
deleted file mode 100644
index 76319c9..0000000
--- a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/signin/BUILD.gn b/ios/chrome/browser/ui/first_run/signin/BUILD.gn
index fcd2cbc..4de89fd8 100644
--- a/ios/chrome/browser/ui/first_run/signin/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/signin/BUILD.gn
@@ -32,8 +32,8 @@
     "//ios/chrome/browser/ui/first_run:constants",
     "//ios/chrome/browser/ui/first_run:screen_delegate",
     "//ios/chrome/browser/ui/first_run:utils",
+    "//ios/chrome/browser/ui/first_run/tos",
     "//ios/chrome/browser/ui/first_run/uma",
-    "//ios/chrome/browser/ui/first_run/welcome",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
index e83b816..8ef0410 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
@@ -29,8 +29,8 @@
 #import "ios/chrome/browser/ui/first_run/signin/signin_screen_consumer.h"
 #import "ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.h"
 #import "ios/chrome/browser/ui/first_run/signin/signin_screen_view_controller.h"
+#import "ios/chrome/browser/ui/first_run/tos/tos_coordinator.h"
 #import "ios/chrome/browser/ui/first_run/uma/uma_coordinator.h"
-#import "ios/chrome/browser/ui/first_run/welcome/tos_coordinator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/first_run/welcome/BUILD.gn b/ios/chrome/browser/ui/first_run/tos/BUILD.gn
similarity index 62%
rename from ios/chrome/browser/ui/first_run/welcome/BUILD.gn
rename to ios/chrome/browser/ui/first_run/tos/BUILD.gn
index 8015dfae..f3c5125d 100644
--- a/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/tos/BUILD.gn
@@ -2,28 +2,22 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("welcome") {
+source_set("tos") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "tos_coordinator.h",
     "tos_coordinator.mm",
   ]
   deps = [
-    ":welcome_ui",
+    ":tos_ui",
     "//base",
-    "//components/metrics",
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/first_run",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
-    "//ios/chrome/browser/ui/first_run:field_trial",
-    "//ios/chrome/browser/ui/first_run:screen_delegate",
-    "//ios/chrome/browser/ui/first_run:utils",
     "//ios/chrome/browser/ui/first_run/uma",
     "//ios/chrome/browser/ui/util:terms_util",
     "//ios/web/common:web_view_creation_util",
@@ -32,7 +26,7 @@
   frameworks = [ "UIKit.framework" ]
 }
 
-source_set("welcome_ui") {
+source_set("tos_ui") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "tos_view_controller.h",
@@ -40,24 +34,12 @@
   ]
   deps = [
     "//base",
-    "//components/metrics",
-    "//components/prefs",
-    "//components/web_resource",
     "//ios/chrome/app/strings",
-    "//ios/chrome/browser/application_context",
-    "//ios/chrome/browser/policy:policy_util",
     "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/first_run:constants",
-    "//ios/chrome/browser/ui/first_run:field_trial",
-    "//ios/chrome/browser/ui/first_run:utils",
-    "//ios/chrome/browser/ui/first_run/uma",
-    "//ios/chrome/browser/ui/util",
     "//ios/chrome/common:string_util",
     "//ios/chrome/common/ui/colors",
-    "//ios/chrome/common/ui/promo_style",
     "//ios/chrome/common/ui/util",
     "//ui/base",
-    "//url",
   ]
   frameworks = [ "UIKit.framework" ]
 }
@@ -67,7 +49,7 @@
   testonly = true
   sources = [ "tos_unittest.mm" ]
   deps = [
-    ":welcome",
+    ":tos",
     "//testing/gtest",
     "//third_party/ocmock",
   ]
diff --git a/ios/chrome/browser/ui/first_run/welcome/tos_coordinator.h b/ios/chrome/browser/ui/first_run/tos/tos_coordinator.h
similarity index 61%
rename from ios/chrome/browser/ui/first_run/welcome/tos_coordinator.h
rename to ios/chrome/browser/ui/first_run/tos/tos_coordinator.h
index 4e7ae5f3..df4bde70 100644
--- a/ios/chrome/browser/ui/first_run/welcome/tos_coordinator.h
+++ b/ios/chrome/browser/ui/first_run/tos/tos_coordinator.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 IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_TOS_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_TOS_COORDINATOR_H_
+#ifndef IOS_CHROME_BROWSER_UI_FIRST_RUN_TOS_TOS_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_FIRST_RUN_TOS_TOS_COORDINATOR_H_
 
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
@@ -12,4 +12,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_TOS_COORDINATOR_H_
+#endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_TOS_TOS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/first_run/welcome/tos_coordinator.mm b/ios/chrome/browser/ui/first_run/tos/tos_coordinator.mm
similarity index 97%
rename from ios/chrome/browser/ui/first_run/welcome/tos_coordinator.mm
rename to ios/chrome/browser/ui/first_run/tos/tos_coordinator.mm
index 08a7b34a..26e833b2 100644
--- a/ios/chrome/browser/ui/first_run/welcome/tos_coordinator.mm
+++ b/ios/chrome/browser/ui/first_run/tos/tos_coordinator.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/first_run/welcome/tos_coordinator.h"
+#import "ios/chrome/browser/ui/first_run/tos/tos_coordinator.h"
 
 #import <WebKit/WebKit.h>
 
@@ -14,7 +14,7 @@
 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/tos_commands.h"
-#import "ios/chrome/browser/ui/first_run/welcome/tos_view_controller.h"
+#import "ios/chrome/browser/ui/first_run/tos/tos_view_controller.h"
 #import "ios/chrome/browser/ui/util/terms_util.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/web/common/web_view_creation_util.h"
diff --git a/ios/chrome/browser/ui/first_run/welcome/tos_unittest.mm b/ios/chrome/browser/ui/first_run/tos/tos_unittest.mm
similarity index 96%
rename from ios/chrome/browser/ui/first_run/welcome/tos_unittest.mm
rename to ios/chrome/browser/ui/first_run/tos/tos_unittest.mm
index 58e3608..f83e8c6 100644
--- a/ios/chrome/browser/ui/first_run/welcome/tos_unittest.mm
+++ b/ios/chrome/browser/ui/first_run/tos/tos_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/first_run/welcome/tos_coordinator.h"
+#import "ios/chrome/browser/ui/first_run/tos/tos_coordinator.h"
 
 #import <WebKit/WebKit.h>
 #import <gtest/gtest.h>
diff --git a/ios/chrome/browser/ui/first_run/welcome/tos_view_controller.h b/ios/chrome/browser/ui/first_run/tos/tos_view_controller.h
similarity index 72%
rename from ios/chrome/browser/ui/first_run/welcome/tos_view_controller.h
rename to ios/chrome/browser/ui/first_run/tos/tos_view_controller.h
index e6b0671d..608ca80f 100644
--- a/ios/chrome/browser/ui/first_run/welcome/tos_view_controller.h
+++ b/ios/chrome/browser/ui/first_run/tos/tos_view_controller.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 IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_TOS_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_TOS_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_FIRST_RUN_TOS_TOS_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_FIRST_RUN_TOS_TOS_VIEW_CONTROLLER_H_
 
 #import <UIKit/UIKit.h>
 
@@ -23,4 +23,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_TOS_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_TOS_TOS_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/first_run/welcome/tos_view_controller.mm b/ios/chrome/browser/ui/first_run/tos/tos_view_controller.mm
similarity index 96%
rename from ios/chrome/browser/ui/first_run/welcome/tos_view_controller.mm
rename to ios/chrome/browser/ui/first_run/tos/tos_view_controller.mm
index 2099fed..729370c2 100644
--- a/ios/chrome/browser/ui/first_run/welcome/tos_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/tos/tos_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/first_run/welcome/tos_view_controller.h"
+#import "ios/chrome/browser/ui/first_run/tos/tos_view_controller.h"
 
 #import <WebKit/WebKit.h>
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index faf6fba..fc5b64bac 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -453,6 +453,10 @@
   if (self.browser->GetBrowserState()->IsOffTheRecord()) {
     return;
   }
+  // Call this before RefreshFeed() to ensure some NTP state configs are reset
+  // before callbacks in repsonse to a feed refresh are called, ensuring the NTP
+  // returns to a state at the top of the surface upon refresh.
+  [self.NTPViewController resetStateUponReload];
   self.discoverFeedService->RefreshFeed(/*feed_visible=*/true);
   [self reloadContentSuggestions];
 }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h
index 0eca0d7..d365a857 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h
@@ -53,6 +53,9 @@
 // Feature flag to enable synthentic capabilities.
 BASE_DECLARE_FEATURE(kEnableFeedSyntheticCapabilities);
 
+// Feature flag to enable sending discover feedback to an updated target
+BASE_DECLARE_FEATURE(kWebFeedFeedbackReroute);
+
 #pragma mark - Feature parameters
 
 // A parameter to indicate whether Reconstructed Templates is enabled for static
@@ -150,4 +153,7 @@
 // from the server, or returns the default value.
 int FollowingFeedHeaderHeight();
 
+// YES if discover feedback is going to be sent to the updated target
+bool IsWebFeedFeedbackRerouteEnabled();
+
 #endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FEATURE_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
index e6d81af..2082d449 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
@@ -63,6 +63,10 @@
              "EnableFeedSyntheticCapabilities",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kWebFeedFeedbackReroute,
+             "WebFeedFeedbackReroute",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 #pragma mark - Feature parameters
 
 const char kDiscoverFeedSRSReconstructedTemplatesEnabled[] =
@@ -175,3 +179,7 @@
                                                 kOverrideFeedHeaderHeight,
                                                 defaultWebChannelsHeaderHeight);
 }
+
+bool IsWebFeedFeedbackRerouteEnabled() {
+  return base::FeatureList::IsEnabled(kWebFeedFeedbackReroute);
+}
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
index c0fe8d3f..71ea13fa 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
@@ -114,6 +114,9 @@
 // Resets hierarchy of views and view controllers.
 - (void)resetViewHierarchy;
 
+// Resets any relevant NTP states due for a content reload.
+- (void)resetStateUponReload;
+
 // Sets the NTP collection view's scroll position to `contentOffset`, unless it
 // is beyond the top of the feed. In that case, sets the scroll position to the
 // top of the feed.
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index 2077bec1..4c93c64 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -237,6 +237,10 @@
   // can open a new tab while an NTP is currently visible. `-viewWillAppear:` is
   // called before the offset can be saved, so `-setContentOffsetToTop` will
   // reset any scrolled position.
+  // It is NOT safe to reset `hasSavedOffsetFromPreviousScrollState` to NO here
+  // because -updateHeightAboveFeedAndScrollToTopIfNeeded calls from async
+  // updates to the Content Suggestions (i.e. MVT, Doodle) can happen after
+  // this.
   if (!self.feedVisible) {
     if (self.hasSavedOffsetFromPreviousScrollState) {
       [self setContentOffset:self.savedScrollOffset];
@@ -505,6 +509,10 @@
   [self.viewControllersAboveFeed removeAllObjects];
 }
 
+- (void)resetStateUponReload {
+  self.hasSavedOffsetFromPreviousScrollState = NO;
+}
+
 - (void)setContentOffsetToTopOfFeed:(CGFloat)contentOffset {
   if (contentOffset < [self offsetWhenScrolledIntoFeed]) {
     [self setContentOffset:contentOffset];
@@ -546,6 +554,10 @@
   // Updating insets can influence contentOffset, so update saved scroll state
   // after it. This handles what the starting offset be with the feed enabled,
   // `-viewWillAppear:` handles when the feed is not enabled.
+  // It is NOT safe to reset `hasSavedOffsetFromPreviousScrollState` to NO here
+  // because -updateHeightAboveFeedAndScrollToTopIfNeeded calls from async
+  // updates to the Content Suggestions (i.e. MVT, Doodle) can happen after
+  // this.
   if (self.hasSavedOffsetFromPreviousScrollState) {
     [self setContentOffset:self.savedScrollOffset];
   }
@@ -582,12 +594,12 @@
   if ([self.ntpContentDelegate isContentHeaderSticky]) {
     [self setInitialFeedHeaderConstraints];
   }
-  // Reset here since none of the view lifecycle callbacks are called reliably
-  // to be able to be used (it seems) (i.e. switching between NTPs where there
-  // is saved scroll state in the destination tab). If the content offset is
-  // being set to the top, it is safe to assume this can be set to NO. Being
-  // called before setSavedContentOffset: is no problem since then it will be
-  // subsequently overriden to YES.
+  // Reset here since none of the view lifecycle callbacks (e.g.
+  // viewDidDisappear) can be reliably used (it seems) (i.e. switching between
+  // NTPs where there is saved scroll state in the destination tab). If the
+  // content offset is being set to the top, it is safe to assume this can be
+  // set to NO. Being called before setSavedContentOffset: is no problem since
+  // then it will be subsequently overriden to YES.
   self.hasSavedOffsetFromPreviousScrollState = NO;
 }
 
@@ -693,6 +705,9 @@
 }
 
 - (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
+  // User has interacted with the surface, so it is safe to assume that a saved
+  // scroll position can now be overriden.
+  self.hasSavedOffsetFromPreviousScrollState = NO;
   [self.overscrollActionsController scrollViewWillBeginDragging:scrollView];
   [self.panGestureHandler scrollViewWillBeginDragging:scrollView];
   self.scrollStartPosition = scrollView.contentOffset.y;
@@ -742,6 +757,13 @@
   // Prevent scrolling back to pre-focus state, making sure we don't have
   // two scrolling animations running at the same time.
   self.collectionShiftingOffset = 0;
+  // Reset here since none of the view lifecycle callbacks are called reliably
+  // to be able to be used (it seems) (i.e. switching between NTPs where there
+  // is saved scroll state in the destination tab). If the content offset is
+  // being set to the top, it is safe to assume this can be set to NO. Being
+  // called before setSavedContentOffset: is no problem since then it will be
+  // subsequently overriden to YES.
+  self.hasSavedOffsetFromPreviousScrollState = NO;
   // Unfocus omnibox without scrolling back.
   [self unfocusOmnibox];
   return YES;
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift
index f012759..a6174f4 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_destination_view.swift
@@ -145,7 +145,7 @@
             .frame(width: Dimensions.newLabelBadgeWidth, height: Dimensions.newLabelBadgeWidth)
             .offset(x: newBadgeOffsetX, y: newBadgeOffsetY)
             .overlay {
-              if let newLabelString = L10NUtils.string(
+              if let newLabelString = L10NUtils.stringWithFixup(
                 forMessageId: IDS_IOS_NEW_LABEL_FEATURE_BADGE)
               {
                 Text(newLabelString)
diff --git a/ios/chrome/browser/ui/post_restore_signin/features.cc b/ios/chrome/browser/ui/post_restore_signin/features.cc
index 8a1a1eb..d20df0d 100644
--- a/ios/chrome/browser/ui/post_restore_signin/features.cc
+++ b/ios/chrome/browser/ui/post_restore_signin/features.cc
@@ -12,17 +12,14 @@
 
 BASE_FEATURE(kIOSNewPostRestoreExperience,
              "IOSNewPostRestoreExperience",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 const char kIOSNewPostRestoreExperienceParam[] =
     "ios-new-post-restore-experience";
 
 PostRestoreSignInType CurrentPostRestoreSignInType() {
-  if (base::FeatureList::IsEnabled(kIOSNewPostRestoreExperience))
-    return base::GetFieldTrialParamByFeatureAsBool(
-               kIOSNewPostRestoreExperience, kIOSNewPostRestoreExperienceParam,
-               false)
-               ? PostRestoreSignInType::kAlert
-               : PostRestoreSignInType::kFullscreen;
+  if (base::FeatureList::IsEnabled(kIOSNewPostRestoreExperience)) {
+    return PostRestoreSignInType::kAlert;
+  }
 
   return PostRestoreSignInType::kDisabled;
 }
diff --git a/ios/chrome/browser/ui/post_restore_signin/post_restore_signin_provider_unittest.mm b/ios/chrome/browser/ui/post_restore_signin/post_restore_signin_provider_unittest.mm
index 7844072..905db27 100644
--- a/ios/chrome/browser/ui/post_restore_signin/post_restore_signin_provider_unittest.mm
+++ b/ios/chrome/browser/ui/post_restore_signin/post_restore_signin_provider_unittest.mm
@@ -57,11 +57,6 @@
     provider_.handler = mock_handler_;
   }
 
-  void EnableFeatureVariationFullscreen() {
-    scoped_feature_list_.InitAndEnableFeature(
-        post_restore_signin::features::kIOSNewPostRestoreExperience);
-  }
-
   void EnableFeatureVariationAlert() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {base::test::FeatureRefAndParams(
@@ -77,12 +72,6 @@
   PostRestoreSignInProvider* provider_;
 };
 
-TEST_F(PostRestoreSignInProviderTest, hasIdentifierFullscreen) {
-  EnableFeatureVariationFullscreen();
-  EXPECT_EQ(provider_.config.identifier,
-            promos_manager::Promo::PostRestoreSignInFullscreen);
-}
-
 TEST_F(PostRestoreSignInProviderTest, hasIdentifierAlert) {
   EnableFeatureVariationAlert();
   EXPECT_EQ(provider_.config.identifier,
@@ -90,7 +79,7 @@
 }
 
 TEST_F(PostRestoreSignInProviderTest, standardPromoAlertDefaultAction) {
-  EnableFeatureVariationFullscreen();
+  EnableFeatureVariationAlert();
   SetupMockHandler();
   OCMExpect([mock_handler_ showSignin:[OCMArg any]]);
   [provider_ standardPromoAlertDefaultAction];
@@ -98,12 +87,12 @@
 }
 
 TEST_F(PostRestoreSignInProviderTest, title) {
-  EnableFeatureVariationFullscreen();
+  EnableFeatureVariationAlert();
   EXPECT_TRUE([[provider_ title] isEqualToString:@"Chrome is Signed Out"]);
 }
 
 TEST_F(PostRestoreSignInProviderTest, message) {
-  EnableFeatureVariationFullscreen();
+  EnableFeatureVariationAlert();
   NSString* expected;
   if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
     expected = @"You were signed out of your account person@example.org as "
@@ -131,7 +120,7 @@
 }
 
 TEST_F(PostRestoreSignInProviderTest, viewController) {
-  EnableFeatureVariationFullscreen();
+  EnableFeatureVariationAlert();
   EXPECT_TRUE(provider_.viewController != nil);
 }
 
diff --git a/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.h b/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.h
index 6ca687b..c872fd6 100644
--- a/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.h
+++ b/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.h
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/prefs/pref_names.h"
 #import "ios/chrome/browser/ui/promos_manager/promos_manager_coordinator.h"
+#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
 #import "ios/chrome/test/scoped_key_window.h"
 #import "testing/platform_test.h"
 
@@ -29,7 +30,7 @@
   // Create pref registry for tests.
   void CreatePrefs();
 
-  std::unique_ptr<TestingPrefServiceSimple> local_state_;
+  IOSChromeScopedTestingLocalState local_state_;
   base::test::ScopedFeatureList scoped_feature_list_;
   PromosManagerCoordinator* coordinator_;
   base::test::TaskEnvironment task_environment_;
diff --git a/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm b/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm
index ef20970..f6dd338 100644
--- a/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm
@@ -39,25 +39,11 @@
 PromosManagerCoordinatorTest::~PromosManagerCoordinatorTest() {}
 
 void PromosManagerCoordinatorTest::CreatePromosManagerCoordinator() {
-  CreatePrefs();
-
   coordinator_ = [[PromosManagerCoordinator alloc]
       initWithBaseViewController:view_controller_
                          browser:browser_.get()];
 }
 
-// Create pref registry for tests.
-void PromosManagerCoordinatorTest::CreatePrefs() {
-  local_state_ = std::make_unique<TestingPrefServiceSimple>();
-
-  local_state_->registry()->RegisterListPref(
-      prefs::kIosPromosManagerImpressions);
-  local_state_->registry()->RegisterListPref(
-      prefs::kIosPromosManagerActivePromos);
-  local_state_->registry()->RegisterListPref(
-      prefs::kIosPromosManagerSingleDisplayActivePromos);
-}
-
 // Tests a provider's standardPromoDismissAction is called when a
 // viewController's dismiss button is pressed.
 TEST_F(PromosManagerCoordinatorTest,
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn
index a757337..bba2b97d 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn
@@ -36,6 +36,14 @@
   ]
 }
 
+source_set("password_checkup_constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "password_checkup_constants.h",
+    "password_checkup_constants.mm",
+  ]
+}
+
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.h b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.h
new file mode 100644
index 0000000..ec34f3a9
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.h
@@ -0,0 +1,18 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_CHECKUP_PASSWORD_CHECKUP_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_CHECKUP_PASSWORD_CHECKUP_CONSTANTS_H_
+
+namespace password_manager {
+
+// URL to the help center article about changing unsafe passwords.
+extern const char kPasswordManagerHelpCenterChangeUnsafePasswordsURL[];
+
+// URL to the help center article about creating strong passwords.
+extern const char kPasswordManagerHelpCenterCreateStrongPasswordsURL[];
+
+}  // namespace password_manager
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_CHECKUP_PASSWORD_CHECKUP_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.mm b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.mm
new file mode 100644
index 0000000..d12d634b
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.mm
@@ -0,0 +1,19 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace password_manager {
+
+const char kPasswordManagerHelpCenterChangeUnsafePasswordsURL[] =
+    "https://support.google.com/accounts/answer/9457609";
+
+const char kPasswordManagerHelpCenterCreateStrongPasswordsURL[] =
+    "https://support.google.com/accounts/answer/32040";
+
+}  // namespace password_manager
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm b/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm
index ca7acbc..1488a02e 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm
+++ b/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm
@@ -146,6 +146,50 @@
       assertWithMatcher:grey_notVisible()];
 }
 
+// Tests that navigating to a page and restarting upon cold start, an NTP page
+// is opened with the Return to Recent Tab tile. Then, subsequently opening a
+// new tab removes the Return To Recent Tab tile from both the new tab's NTP and
+// the Start NTP.
+- (void)testOpeningNewTabRemovesReturnToRecenTabTile {
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+  const GURL destinationUrl = self.testServer->GetURL("/pony.html");
+  [ChromeEarlGrey loadURL:destinationUrl];
+
+  [[AppLaunchManager sharedManager] backgroundAndForegroundApp];
+
+  // Give time for NTP to be fully loaded so all elements are accessible.
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(0.5));
+  GREYAssertEqual([ChromeEarlGrey mainTabCount], 2,
+                  @"Two tabs were expected to be open");
+  // Assert NTP is visible by checking that the fake omnibox is here.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
+                                   IDS_IOS_RETURN_TO_RECENT_TAB_TITLE))]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  [ChromeEarlGreyUI openNewTab];
+  [ChromeEarlGreyUI waitForAppToIdle];
+
+  // Assert that Return to Recent Tab has been removed.
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
+                                   IDS_IOS_RETURN_TO_RECENT_TAB_TITLE))]
+      assertWithMatcher:grey_notVisible()];
+
+  // Close current tab to go back to the previous Start NTP.
+  [ChromeEarlGrey closeCurrentTab];
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
+                                   IDS_IOS_RETURN_TO_RECENT_TAB_TITLE))]
+      assertWithMatcher:grey_notVisible()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPLogo()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+}
+
 // Tests that the Return To Recent Tab tile is removed after opening the tab
 // grid (i.e. switching away from the Start Surface).
 - (void)testReturnToRecenTabTileRemovedAfterOpeningTabGrid {
diff --git a/ios/chrome/browser/ui/webui/policy/BUILD.gn b/ios/chrome/browser/ui/webui/policy/BUILD.gn
index 04b564b9..f4fe746 100644
--- a/ios/chrome/browser/ui/webui/policy/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/policy/BUILD.gn
@@ -22,6 +22,7 @@
     "//components/prefs",
     "//components/strings",
     "//components/version_info",
+    "//components/version_ui",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/application_context",
     "//ios/chrome/browser/browser_state",
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui.mm b/ios/chrome/browser/ui/webui/policy/policy_ui.mm
index b37b092..34fd334 100644
--- a/ios/chrome/browser/ui/webui/policy/policy_ui.mm
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui.mm
@@ -7,11 +7,18 @@
 #import <memory>
 #import <string>
 
+#import "base/json/json_writer.h"
 #import "components/grit/policy_resources.h"
+#import "components/policy/core/common/policy_logger.h"
+#import "components/strings/grit/components_chromium_strings.h"
+#import "components/strings/grit/components_google_chrome_strings.h"
 #import "components/strings/grit/components_strings.h"
+#import "components/version_info/version_info.h"
+#import "components/version_ui/version_handler_helper.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/ui/webui/policy/policy_ui_handler.h"
 #import "ios/chrome/browser/url/chrome_url_constants.h"
+#import "ios/chrome/grit/ios_chromium_strings.h"
 #import "ios/web/public/webui/web_ui_ios.h"
 #import "ios/web/public/webui/web_ui_ios_data_source.h"
 #import "ios/web/public/webui/web_ui_ios_message_handler.h"
@@ -23,6 +30,19 @@
 
 namespace {
 
+// Returns the version information to be displayed on the chrome://policy/logs
+// page.
+base::Value::Dict GetVersionInfo() {
+  base::Value::Dict version_info;
+
+  version_info.Set("revision", version_info::GetLastChange());
+  version_info.Set("version", version_info::GetVersionNumber());
+  version_info.Set("deviceOs", "iOS");
+  version_info.Set("variations", version_ui::GetVariationsList());
+
+  return version_info;
+}
+
 web::WebUIIOSDataSource* CreatePolicyUIHtmlSource() {
   web::WebUIIOSDataSource* source =
       web::WebUIIOSDataSource::Create(kChromeUIPolicyHost);
@@ -89,6 +109,20 @@
 
   };
   source->AddLocalizedStrings(kStrings);
+
+  // Localized strings for chrome://policy/logs.
+  static constexpr webui::LocalizedString kPolicyLogsStrings[] = {
+      {"browserName", IDS_IOS_PRODUCT_NAME},
+      {"exportLogsJSON", IDS_EXPORT_POLICY_LOGS_JSON},
+      {"logsTitle", IDS_POLICY_LOGS_TITLE},
+      {"os", IDS_VERSION_UI_OS},
+      {"refreshLogs", IDS_REFRESH_POLICY_LOGS},
+      {"revision", IDS_VERSION_UI_REVISION},
+      {"versionInfoLabel", IDS_VERSION_INFO},
+      {"variations", IDS_VERSION_UI_VARIATIONS},
+  };
+  source->AddLocalizedStrings(kPolicyLogsStrings);
+
   source->UseStringsJs();
 
   source->AddBoolean("hideExportButton", true);
@@ -110,6 +144,21 @@
   source->AddResourcePath("policy_table.js", IDR_POLICY_POLICY_TABLE_JS);
   source->AddResourcePath("status_box.html.js", IDR_POLICY_STATUS_BOX_HTML_JS);
   source->AddResourcePath("status_box.js", IDR_POLICY_STATUS_BOX_JS);
+
+  source->AddBoolean(
+      "loggingEnabled",
+      policy::PolicyLogger::GetInstance()->IsPolicyLoggingEnabled());
+
+  if (policy::PolicyLogger::GetInstance()->IsPolicyLoggingEnabled()) {
+    std::string variations_json_value;
+    base::JSONWriter::Write(GetVersionInfo(), &variations_json_value);
+    source->AddString("versionInfo", variations_json_value);
+  }
+  source->AddResourcePath("logs/policy_logs.js",
+                          IDR_POLICY_LOGS_POLICY_LOGS_JS);
+  source->AddResourcePath("logs/", IDR_POLICY_LOGS_POLICY_LOGS_HTML);
+  source->AddResourcePath("logs", IDR_POLICY_LOGS_POLICY_LOGS_HTML);
+
   source->SetDefaultResource(IDR_POLICY_POLICY_HTML);
   source->EnableReplaceI18nInJS();
   return source;
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h
index c3c8d7af..46d820f9 100644
--- a/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h
@@ -73,6 +73,10 @@
   // Called to handle the "uploadReport" WebUI message.
   void HandleUploadReport(const base::Value::List& args);
 
+  // Called to handle the "getPolicyLogs" WebUI message from
+  // chrome://policy/logs.
+  void HandleGetPolicyLogs(const base::Value::List& args);
+
   // Send information about the current policy values to the UI. For each policy
   // whose value has been set, dictionaries containing the value and additional
   // metadata are sent.
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm
index 00b013a..ffb5055 100644
--- a/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm
@@ -23,6 +23,7 @@
 #import "components/policy/core/browser/webui/machine_level_user_cloud_policy_status_provider.h"
 #import "components/policy/core/browser/webui/policy_webui_constants.h"
 #import "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#import "components/policy/core/common/policy_logger.h"
 #import "components/policy/core/common/policy_map.h"
 #import "components/policy/core/common/policy_types.h"
 #import "components/policy/core/common/schema.h"
@@ -143,6 +144,10 @@
   web_ui()->RegisterMessageCallback(
       "uploadReport", base::BindRepeating(&PolicyUIHandler::HandleUploadReport,
                                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getPolicyLogs",
+      base::BindRepeating(&PolicyUIHandler::HandleGetPolicyLogs,
+                          base::Unretained(this)));
 }
 
 void PolicyUIHandler::HandleCopyPoliciesJson(const base::Value::List& args) {
@@ -166,6 +171,12 @@
   }
 }
 
+void PolicyUIHandler::HandleGetPolicyLogs(const base::Value::List& args) {
+  DCHECK(policy::PolicyLogger::GetInstance()->IsPolicyLoggingEnabled());
+  web_ui()->ResolveJavascriptCallback(
+      args[0], policy::PolicyLogger::GetInstance()->GetAsList());
+}
+
 std::string PolicyUIHandler::GetPoliciesAsJson() {
   auto client = std::make_unique<PolicyConversionsClientIOS>(
       ChromeBrowserState::FromWebUIIOS(web_ui()));
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index caa6de7..02958f5 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -305,7 +305,7 @@
     "//ios/chrome/browser/ui/elements:unit_tests",
     "//ios/chrome/browser/ui/first_run:unit_tests",
     "//ios/chrome/browser/ui/first_run:unit_tests",
-    "//ios/chrome/browser/ui/first_run/welcome:unit_tests",
+    "//ios/chrome/browser/ui/first_run/tos:unit_tests",
     "//ios/chrome/browser/ui/fullscreen:unit_tests",
     "//ios/chrome/browser/ui/gestures:unit_tests",
     "//ios/chrome/browser/ui/history:unit_tests",
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 7bb7715..97d0da03 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-efd0ff2b5e680245106e57c8714734943c36d605
\ No newline at end of file
+ab0ee9a3e269c2d77342afa0bae165eb2c4d402b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 9db977ebc..69dea7e 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-c89a3d99eebb59ad82b08c4145a9beea4fcdad60
\ No newline at end of file
+51bfb281a75f37c4f0ea5f97f9250d2234b4122f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 323ae04..0013894 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-99082e4c0f305f976eb90d262a7199c881400763
\ No newline at end of file
+93da811cafe5edbe259490e615335f6d08a298a8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index b69b1db..41c7c8f 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-885705a2c128d40b2d72cccfcfddbc061986c331
\ No newline at end of file
+d28415407c0377fd7e7df061a8aeae86b3fdc310
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 117c1475..97600a1 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-2004981c7aead5a0f4d94026eb9910b6c4948eae
\ No newline at end of file
+c5d41b90a2974132f5381e6ff0f7c8b9e53ba3ab
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 8221bae6..6016d475 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-469371fa82805156f6b8d44ef8d4b0dd3b9e8994
\ No newline at end of file
+f1103cf3238d881459fafddb662f54b751eea9e6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 737bf46a..dbd5cf7 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-81dc53bf9562ce374ceaaa5c1bb422bc98725252
\ No newline at end of file
+0c9879c49c3eaddfc319d35d3e7fe62b678681ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 77c6528..a20c192 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-25eeede3aa36dca1f0b3ed8fecfcca373ae6a823
\ No newline at end of file
+6e742042ea7d88b18aa060cc37bae2718334393e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 1a73aa94..248806f 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-05ba9b14904557d07f85cea2730d9d77d71f3c7f
\ No newline at end of file
+61cfe27d1ab74f3316218a65b99f44eea2b75739
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index db190252..8762b39 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-1b8ef59cdefd6cb04fdf97b94b31e582e9c183be
\ No newline at end of file
+c85c5352816a426a732b37e380dfe37d6882e9e7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index abea713..3206171 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e73a0af6e9cda480c8776a84a439c0a8a5b72682
\ No newline at end of file
+06772e6472ca18cb5a88fba1845012853a9e7e57
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 016bb73..a6239f20 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d79e8da3d490432c8e4e6a4fa3f25594dd0ada56
\ No newline at end of file
+baf2ef72971470f8d47804f98ad7835d47f342e5
\ No newline at end of file
diff --git a/ios/web/common/features.h b/ios/web/common/features.h
index fb06720c..2b92eb2 100644
--- a/ios/web/common/features.h
+++ b/ios/web/common/features.h
@@ -82,6 +82,9 @@
 // Feature flag to prevent navigation without user interaction.
 BASE_DECLARE_FEATURE(kPreventNavigationWithoutUserInteraction);
 
+// Feature flag to enable Web Inspector support.
+BASE_DECLARE_FEATURE(kEnableWebInspector);
+
 // When true, user control for camera and/or microphone access should be
 // enabled.
 bool IsMediaPermissionsControlEnabled();
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index e5e49bcc..12f32b15 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -85,6 +85,10 @@
              "PreventNavigationWithoutUserInteraction",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kEnableWebInspector,
+             "EnableWebInspector",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 bool IsMediaPermissionsControlEnabled() {
   if (@available(iOS 15, *)) {
     return base::FeatureList::IsEnabled(kMediaPermissionsControl);
diff --git a/ios/web/find_in_page/find_in_page_js_unittest.mm b/ios/web/find_in_page/find_in_page_js_unittest.mm
index f61a6cb8..fe32964c 100644
--- a/ios/web/find_in_page/find_in_page_js_unittest.mm
+++ b/ios/web/find_in_page/find_in_page_js_unittest.mm
@@ -10,6 +10,7 @@
 #import "base/run_loop.h"
 #import "base/test/ios/wait_util.h"
 #import "base/time/time.h"
+#import "base/values.h"
 #import "ios/web/find_in_page/find_in_page_constants.h"
 #import "ios/web/find_in_page/find_in_page_java_script_feature.h"
 #import "ios/web/js_messaging/java_script_feature_manager.h"
@@ -23,6 +24,7 @@
 #import "ios/web/public/web_state.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 #import "testing/gtest_mac.h"
+#import "third_party/abseil-cpp/absl/types/optional.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -282,7 +284,7 @@
       base::BindOnce(^(const base::Value* result) {
         highlight_done = true;
         context_string =
-            result->FindKey(kSelectAndScrollResultContextString)->GetString();
+            *result->GetDict().FindString(kSelectAndScrollResultContextString);
       }),
       kWaitForJSCompletionTimeout);
   ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
@@ -328,7 +330,7 @@
       base::BindOnce(^(const base::Value* result) {
         highlight_done = true;
         context_string =
-            result->FindKey(kSelectAndScrollResultContextString)->GetString();
+            *result->GetDict().FindString(kSelectAndScrollResultContextString);
       }),
       kWaitForJSCompletionTimeout);
   ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
@@ -348,7 +350,7 @@
       content_world_, base::BindOnce(^(const base::Value* result) {
         highlight_done = true;
         context_string =
-            result->FindKey(kSelectAndScrollResultContextString)->GetString();
+            *result->GetDict().FindString(kSelectAndScrollResultContextString);
       }),
       kWaitForJSCompletionTimeout);
   ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
@@ -624,13 +626,15 @@
       base::BindOnce(^(const base::Value* result) {
         ASSERT_TRUE(result);
         ASSERT_TRUE(result->is_dict());
-        const base::Value* count =
-            result->FindKey(kSelectAndScrollResultMatches);
-        ASSERT_TRUE(count->is_double());
-        ASSERT_EQ(2.0, count->GetDouble());
-        const base::Value* index = result->FindKey(kSelectAndScrollResultIndex);
-        ASSERT_TRUE(index->is_double());
-        ASSERT_EQ(0.0, index->GetDouble());
+        const base::Value::Dict& result_dict = result->GetDict();
+        const absl::optional<double> count =
+            result_dict.FindDouble(kSelectAndScrollResultMatches);
+        ASSERT_TRUE(count);
+        ASSERT_EQ(2.0, count.value());
+        const absl::optional<double> index =
+            result_dict.FindDouble(kSelectAndScrollResultIndex);
+        ASSERT_TRUE(index);
+        ASSERT_EQ(0.0, index.value());
         message_received = true;
       }),
       kWaitForJSCompletionTimeout);
@@ -673,9 +677,11 @@
       base::BindOnce(^(const base::Value* result) {
         ASSERT_TRUE(result);
         ASSERT_TRUE(result->is_dict());
-        const base::Value* index = result->FindKey(kSelectAndScrollResultIndex);
-        ASSERT_TRUE(index->is_double());
-        EXPECT_EQ(3.0, index->GetDouble());
+        const base::Value::Dict& result_dict = result->GetDict();
+        const absl::optional<double> index =
+            result_dict.FindDouble(kSelectAndScrollResultIndex);
+        ASSERT_TRUE(index);
+        EXPECT_EQ(3.0, index.value());
         select_last_match_message_received = true;
       }),
       kWaitForJSCompletionTimeout);
@@ -695,13 +701,15 @@
       content_world_, base::BindOnce(^(const base::Value* result) {
         ASSERT_TRUE(result);
         ASSERT_TRUE(result->is_dict());
-        const base::Value* index = result->FindKey(kSelectAndScrollResultIndex);
-        ASSERT_TRUE(index->is_double());
+        const base::Value::Dict& result_dict = result->GetDict();
+        const absl::optional<double> index =
+            result_dict.FindDouble(kSelectAndScrollResultIndex);
+        ASSERT_TRUE(index);
         // Since there are only two visible matches now and this
         // kFindInPageSelectAndScrollToMatch call is asking Find in Page to
         // traverse to a previous match, Find in Page should look for the next
         // previous visible match. This happens to be the 2nd match.
-        EXPECT_EQ(1.0, index->GetDouble());
+        EXPECT_EQ(1.0, index.value());
         select_third_match_message_received = true;
       }),
       kWaitForJSCompletionTimeout);
diff --git a/ios/web/find_in_page/java_script_find_in_page_manager_impl.mm b/ios/web/find_in_page/java_script_find_in_page_manager_impl.mm
index bef0f22..c2e7206 100644
--- a/ios/web/find_in_page/java_script_find_in_page_manager_impl.mm
+++ b/ios/web/find_in_page/java_script_find_in_page_manager_impl.mm
@@ -18,6 +18,7 @@
 #import "ios/web/public/thread/web_task_traits.h"
 #import "ios/web/public/thread/web_thread.h"
 #import "ios/web/web_state/web_state_impl.h"
+#import "third_party/abseil-cpp/absl/types/optional.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -218,10 +219,12 @@
     const base::Value* result) {
   std::string match_context_string;
   if (result && result->is_dict()) {
+    const base::Value::Dict& result_dict = result->GetDict();
     // Get updated match count.
-    const base::Value* matches = result->FindKey(kSelectAndScrollResultMatches);
-    if (matches && matches->is_double()) {
-      int match_count = static_cast<int>(matches->GetDouble());
+    const absl::optional<double> matches =
+        result_dict.FindDouble(kSelectAndScrollResultMatches);
+    if (matches) {
+      int match_count = static_cast<int>(matches.value());
       if (match_count != last_find_request_.GetMatchCountForSelectedFrame()) {
         last_find_request_.SetMatchCountForSelectedFrame(match_count);
         if (delegate_) {
@@ -232,17 +235,17 @@
       }
     }
     // Get updated currently selected index.
-    const base::Value* index = result->FindKey(kSelectAndScrollResultIndex);
-    if (index && index->is_double()) {
-      int current_index = static_cast<int>(index->GetDouble());
+    const absl::optional<double> index =
+        result_dict.FindDouble(kSelectAndScrollResultIndex);
+    if (index) {
+      int current_index = static_cast<int>(index.value());
       last_find_request_.SetCurrentSelectedMatchFrameIndex(current_index);
     }
     // Get context string.
-    const base::Value* context_string =
-        result->FindKey(kSelectAndScrollResultContextString);
-    if (context_string && context_string->is_string()) {
-      match_context_string =
-          static_cast<std::string>(context_string->GetString());
+    const std::string* context_string =
+        result_dict.FindString(kSelectAndScrollResultContextString);
+    if (context_string) {
+      match_context_string = *context_string;
     }
   }
   if (delegate_) {
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn
index fa2fb668..ffb2ef2 100644
--- a/ios/web/navigation/BUILD.gn
+++ b/ios/web/navigation/BUILD.gn
@@ -92,6 +92,7 @@
     "crw_navigation_item_holder.mm",
     "navigation_context_impl.h",
     "navigation_context_impl.mm",
+    "navigation_initiation_type.h",
     "navigation_item_impl.h",
     "navigation_item_impl.mm",
     "navigation_manager_delegate.h",
diff --git a/ios/web/navigation/navigation_initiation_type.h b/ios/web/navigation/navigation_initiation_type.h
new file mode 100644
index 0000000..f571830
--- /dev/null
+++ b/ios/web/navigation/navigation_initiation_type.h
@@ -0,0 +1,35 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_NAVIGATION_NAVIGATION_INITIATION_TYPE_H_
+#define IOS_WEB_NAVIGATION_NAVIGATION_INITIATION_TYPE_H_
+
+namespace web {
+
+// Defines the ways how a pending navigation can be initiated.
+enum class NavigationInitiationType {
+  // Navigation initiation type is only valid for pending navigations, use NONE
+  // if a navigation is already committed.
+  NONE = 0,
+
+  // Navigation was initiated by the browser by calling NavigationManager
+  // methods. Examples of methods which cause browser-initiated navigations
+  // include:
+  //  * NavigationManager::Reload()
+  //  * NavigationManager::GoBack()
+  //  * NavigationManager::GoForward()
+  BROWSER_INITIATED,
+
+  // Navigation was initiated by renderer. Examples of renderer-initiated
+  // navigations include:
+  //  * <a> link click
+  //  * changing window.location.href
+  //  * redirect via the <meta http-equiv="refresh"> tag
+  //  * using window.history.pushState
+  RENDERER_INITIATED,
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_NAVIGATION_INITIATION_TYPE_H_
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
index 1586479..dc69045 100644
--- a/ios/web/navigation/navigation_manager_impl.h
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "ios/web/navigation/navigation_initiation_type.h"
 #import "ios/web/navigation/navigation_item_impl.h"
 #include "ios/web/navigation/synthesized_session_restore.h"
 #include "ios/web/navigation/time_smoother.h"
@@ -41,29 +42,6 @@
 // restoration.
 extern const char kRestoreNavigationTime[];
 
-// Defines the ways how a pending navigation can be initiated.
-enum class NavigationInitiationType {
-  // Navigation initiation type is only valid for pending navigations, use NONE
-  // if a navigation is already committed.
-  NONE = 0,
-
-  // Navigation was initiated by the browser by calling NavigationManager
-  // methods. Examples of methods which cause browser-initiated navigations
-  // include:
-  //  * NavigationManager::Reload()
-  //  * NavigationManager::GoBack()
-  //  * NavigationManager::GoForward()
-  BROWSER_INITIATED,
-
-  // Navigation was initiated by renderer. Examples of renderer-initiated
-  // navigations include:
-  //  * <a> link click
-  //  * changing window.location.href
-  //  * redirect via the <meta http-equiv="refresh"> tag
-  //  * using window.history.pushState
-  RENDERER_INITIATED,
-};
-
 // WKBackForwardList-based implementation of NavigationManager.
 // Generally mirrors upstream's NavigationController.
 //
diff --git a/ios/web/web_state/web_view_internal_creation_util.mm b/ios/web/web_state/web_view_internal_creation_util.mm
index 4bed9f7..ce343731 100644
--- a/ios/web/web_state/web_view_internal_creation_util.mm
+++ b/ios/web/web_state/web_view_internal_creation_util.mm
@@ -6,6 +6,7 @@
 
 #import "base/check_op.h"
 #import "base/strings/sys_string_conversions.h"
+#import "ios/web/common/features.h"
 #import "ios/web/public/web_client.h"
 #import "ios/web/web_state/crw_web_view.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
@@ -73,7 +74,10 @@
   web_view.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
 
   if (@available(iOS 16.4, *)) {
-    web_view.inspectable = web::GetWebClient()->EnableWebInspector();
+    if (base::FeatureList::IsEnabled(features::kEnableWebInspector) &&
+        web::GetWebClient()->EnableWebInspector()) {
+      web_view.inspectable = YES;
+    }
   }
 
   return web_view;
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index fc84ae0..bdf9889 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -208,7 +208,6 @@
   // Rephrase the field aec_supported to properly reflect its meaning in this
   // context (since it currently signals whether an CrAS APM with tuned settings
   // is available).
-  // TODO(crbug.com/1307680): add unit tests and caching cras_util_ results.
   const bool tuned_system_apm_available = cras_util_->CrasGetAecSupported();
 
   // Don't use the system AEC if it is deactivated for this group ID. Also never
@@ -218,12 +217,8 @@
       (tuned_system_apm_available && tuned_system_aec_allowed) ||
       enforce_system_aec;
 
-  // TODO(b/266242770): Reintroduce the scheme for setting this to follow what
-  // was previously done in (the now removed)
-  // media/audio/cras/audio_manager_chromeos.cc. Until then, the NS and AGC
-  // effects are hardcoded to never run in CRAS.
-  bool system_ns_supported = false;
-  bool system_agc_supported = false;
+  bool system_ns_supported = cras_util_->CrasGetNsSupported();
+  bool system_agc_supported = cras_util_->CrasGetAgcSupported();
 
   int aec_group_id = cras_util_->CrasGetAecGroupId();
   if (!use_system_aec || IsSystemAecDeactivated(aec_group_id)) {
diff --git a/media/audio/cras/audio_manager_cras_unittest.cc b/media/audio/cras/audio_manager_cras_unittest.cc
index 39f9bdf6..edcaace6 100644
--- a/media/audio/cras/audio_manager_cras_unittest.cc
+++ b/media/audio/cras/audio_manager_cras_unittest.cc
@@ -11,8 +11,8 @@
 #include "media/audio/cras/cras_util.h"
 #include "media/audio/fake_audio_log_factory.h"
 #include "media/audio/test_audio_thread.h"
-#include "media/base/media_switches.h"
 #include "media/base/limits.h"
+#include "media/base/media_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -33,6 +33,8 @@
   MOCK_METHOD(int, CrasGetAecSupported, (), (override));
   MOCK_METHOD(int, CrasGetAecGroupId, (), (override));
   MOCK_METHOD(int, CrasGetDefaultOutputBufferSize, (), (override));
+  MOCK_METHOD(int, CrasGetNsSupported, (), (override));
+  MOCK_METHOD(int, CrasGetAgcSupported, (), (override));
 };
 
 class AudioManagerCrasUnderTest : public AudioManagerCras {
@@ -397,19 +399,35 @@
          params.effects() & AudioParameters::ECHO_CANCELLER;
 }
 
+bool DspNsAllowed(const AudioParameters& params) {
+  return params.effects() & AudioParameters::ALLOW_DSP_NOISE_SUPPRESSION &&
+         params.effects() & AudioParameters::NOISE_SUPPRESSION;
+}
+
+bool DspAgcAllowed(const AudioParameters& params) {
+  return params.effects() & AudioParameters::ALLOW_DSP_AUTOMATIC_GAIN_CONTROL &&
+         params.effects() & AudioParameters::AUTOMATIC_GAIN_CONTROL;
+}
+
 class AudioManagerCrasTestAEC
     : public AudioManagerCrasTest,
-      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
  protected:
   void SetUp() override {
     std::unique_ptr<MockCrasUtil> util = std::make_unique<MockCrasUtil>();
     auto aec_supported = std::get<0>(GetParam());
     auto aec_group = std::get<1>(GetParam());
+    auto ns_supported = std::get<2>(GetParam());
+    auto agc_supported = std::get<3>(GetParam());
 
     EXPECT_CALL(*util, CrasGetAecSupported())
         .WillOnce(testing::Return(aec_supported));
     EXPECT_CALL(*util, CrasGetAecGroupId())
         .WillOnce(testing::Return(aec_group));
+    EXPECT_CALL(*util, CrasGetNsSupported())
+        .WillOnce(testing::Return(ns_supported));
+    EXPECT_CALL(*util, CrasGetAgcSupported())
+        .WillOnce(testing::Return(agc_supported));
 
     audio_manager_->SetCrasUtil(std::move(util));
   }
@@ -419,8 +437,9 @@
     AllInputParameters,
     AudioManagerCrasTestAEC,
     ::testing::Combine(::testing::Values(false, true),
-                       ::testing::Values(kNoAecFlaggedGroupId,
-                                         kAecTestGroupId)));
+                       ::testing::Values(kNoAecFlaggedGroupId, kAecTestGroupId),
+                       ::testing::Values(false, true),
+                       ::testing::Values(false, true)));
 
 TEST_P(AudioManagerCrasTestAEC, DefaultBehavior) {
   AudioParameters params = audio_manager_->GetInputStreamParameters("");
@@ -428,13 +447,8 @@
 
   EXPECT_TRUE(ExperimentalAecActive(params));
   EXPECT_EQ(AecActive(params), aec_supported);
-  if (aec_supported) {
-    EXPECT_FALSE(NsActive(params));
-    EXPECT_FALSE(AgcActive(params));
-  } else {
-    EXPECT_FALSE(NsActive(params));
-    EXPECT_FALSE(AgcActive(params));
-  }
+  EXPECT_FALSE(NsActive(params));
+  EXPECT_FALSE(AgcActive(params));
 }
 
 TEST_P(AudioManagerCrasTestAEC, DefaultBehaviorSystemAecEnforcedByPolicy) {
@@ -513,8 +527,7 @@
        BehaviorWithCrOSEnforceSystemAecNsAgcAndDisallowedSystemAec) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      {{media::kCrOSEnforceSystemAecNsAgc, {}}},
-      {{media::kCrOSSystemAEC}});
+      {{media::kCrOSEnforceSystemAecNsAgc, {}}}, {{media::kCrOSSystemAEC}});
   AudioParameters params = audio_manager_->GetInputStreamParameters("");
 
   auto aec_supported = std::get<0>(GetParam());
@@ -530,6 +543,64 @@
   }
 }
 
+TEST_P(AudioManagerCrasTestAEC, BehaviorWithCrOSEnforceSystemAecNs) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(media::kCrOSEnforceSystemAecNs);
+  AudioParameters params = audio_manager_->GetInputStreamParameters("");
+
+  auto aec_supported = std::get<0>(GetParam());
+  auto agc_supported = std::get<3>(GetParam());
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_TRUE(NsActive(params));
+    EXPECT_EQ(AgcActive(params), agc_supported);
+  }
+}
+
+TEST_P(AudioManagerCrasTestAEC, BehaviorWithCrOSEnforceSystemAecAgc) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(media::kCrOSEnforceSystemAecAgc);
+  AudioParameters params = audio_manager_->GetInputStreamParameters("");
+
+  auto aec_supported = std::get<0>(GetParam());
+  auto ns_supported = std::get<2>(GetParam());
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_EQ(NsActive(params), ns_supported);
+    EXPECT_TRUE(AgcActive(params));
+  }
+}
+
+TEST_P(AudioManagerCrasTestAEC, BehaviorWithCrOSEnforceSystemAec) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(media::kCrOSEnforceSystemAec);
+  AudioParameters params = audio_manager_->GetInputStreamParameters("");
+
+  auto aec_supported = std::get<0>(GetParam());
+  auto ns_supported = std::get<2>(GetParam());
+  auto agc_supported = std::get<3>(GetParam());
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_EQ(NsActive(params), ns_supported);
+    EXPECT_EQ(AgcActive(params), agc_supported);
+  }
+}
+
 class AudioManagerCrasTestDSP
     : public AudioManagerCrasTest,
       public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
@@ -560,6 +631,8 @@
 
     EXPECT_CALL(*util, CrasGetAecSupported()).WillOnce(testing::Return(false));
     EXPECT_CALL(*util, CrasGetAecGroupId()).WillOnce(testing::Return(0));
+    EXPECT_CALL(*util, CrasGetNsSupported()).WillOnce(testing::Return(false));
+    EXPECT_CALL(*util, CrasGetAgcSupported()).WillOnce(testing::Return(false));
 
     audio_manager_->SetCrasUtil(std::move(util));
   }
@@ -582,6 +655,8 @@
   AudioParameters params = audio_manager_->GetInputStreamParameters("");
 
   EXPECT_FALSE(DspAecAllowed(params));
+  EXPECT_FALSE(DspNsAllowed(params));
+  EXPECT_FALSE(DspAgcAllowed(params));
 }
 
 TEST_P(AudioManagerCrasTestDSP, BehaviorWithCrOSEnforceSystemAec) {
@@ -592,6 +667,8 @@
 
   EXPECT_TRUE(DspAecAllowed(params) && aec_on_dsp_allowed_ ||
               !DspAecAllowed(params) && !aec_on_dsp_allowed_);
+  EXPECT_FALSE(DspNsAllowed(params));
+  EXPECT_FALSE(DspAgcAllowed(params));
 }
 
 TEST_P(AudioManagerCrasTestDSP, BehaviorWithCrOSEnforceSystemAecNs) {
@@ -602,6 +679,9 @@
 
   EXPECT_TRUE(DspAecAllowed(params) && aec_on_dsp_allowed_ ||
               !DspAecAllowed(params) && !aec_on_dsp_allowed_);
+  EXPECT_TRUE(DspNsAllowed(params) && ns_on_dsp_allowed_ ||
+              !DspNsAllowed(params) && !ns_on_dsp_allowed_);
+  EXPECT_FALSE(DspAgcAllowed(params));
 }
 
 TEST_P(AudioManagerCrasTestDSP, BehaviorWithCrOSEnforceSystemAecAgc) {
@@ -612,6 +692,9 @@
 
   EXPECT_TRUE(DspAecAllowed(params) && aec_on_dsp_allowed_ ||
               !DspAecAllowed(params) && !aec_on_dsp_allowed_);
+  EXPECT_FALSE(DspNsAllowed(params));
+  EXPECT_TRUE(DspAgcAllowed(params) && agc_on_dsp_allowed_ ||
+              !DspAgcAllowed(params) && !agc_on_dsp_allowed_);
 }
 
 TEST_P(AudioManagerCrasTestDSP, BehaviorWithCrOSEnforceSystemAecNsAgc) {
@@ -622,6 +705,10 @@
 
   EXPECT_TRUE(DspAecAllowed(params) && aec_on_dsp_allowed_ ||
               !DspAecAllowed(params) && !aec_on_dsp_allowed_);
+  EXPECT_TRUE(DspNsAllowed(params) && ns_on_dsp_allowed_ ||
+              !DspNsAllowed(params) && !ns_on_dsp_allowed_);
+  EXPECT_TRUE(DspAgcAllowed(params) && agc_on_dsp_allowed_ ||
+              !DspAgcAllowed(params) && !agc_on_dsp_allowed_);
 }
 
 }  // namespace
diff --git a/media/audio/cras/cras_util.cc b/media/audio/cras/cras_util.cc
index cb1b41a..01e28bd 100644
--- a/media/audio/cras/cras_util.cc
+++ b/media/audio/cras/cras_util.cc
@@ -184,6 +184,37 @@
 
 CrasUtil::~CrasUtil() = default;
 
+bool CrasUtil::CacheEffects() {
+  libcras_client* client = CrasConnect();
+  if (!client) {
+    LOG(ERROR) << "Failed to cache effects";
+    return false;
+  }
+  if (libcras_client_get_aec_supported(client, &aec_supported_) < 0) {
+    LOG(ERROR) << "Fail to query AEC supported";
+    aec_supported_ = false;
+  }
+  if (libcras_client_get_agc_supported(client, &agc_supported_) < 0) {
+    LOG(ERROR) << "Fail to query AGC supported";
+    agc_supported_ = false;
+  }
+  if (libcras_client_get_ns_supported(client, &ns_supported_) < 0) {
+    LOG(ERROR) << "Fail to query NS supported";
+    ns_supported_ = false;
+  }
+  if (libcras_client_get_aec_group_id(client, &aec_group_id_) < 0) {
+    LOG(ERROR) << "Fail to query AEC group ID";
+    aec_group_id_ = -1;  // The default group ID is -1
+  }
+  if (libcras_client_get_default_output_buffer_size(
+          client, &default_output_buffer_size_) < 0) {
+    LOG(ERROR) << "Fail to query default output buffer size";
+    default_output_buffer_size_ = 0;
+  }
+  CrasDisconnect(&client);
+  return true;
+}
+
 std::vector<CrasDevice> CrasUtil::CrasGetAudioDevices(DeviceType type) {
   std::vector<CrasDevice> devices;
 
@@ -236,42 +267,38 @@
 }
 
 int CrasUtil::CrasGetAecSupported() {
-  libcras_client* client = CrasConnect();
-  if (!client) {
-    return 0;
+  if (!cras_effects_cached_) {
+    cras_effects_cached_ = CacheEffects();
   }
+  return aec_supported_;
+}
 
-  int supported;
-  libcras_client_get_aec_supported(client, &supported);
-  CrasDisconnect(&client);
+int CrasUtil::CrasGetAgcSupported() {
+  if (!cras_effects_cached_) {
+    cras_effects_cached_ = CacheEffects();
+  }
+  return agc_supported_;
+}
 
-  return supported;
+int CrasUtil::CrasGetNsSupported() {
+  if (!cras_effects_cached_) {
+    cras_effects_cached_ = CacheEffects();
+  }
+  return ns_supported_;
 }
 
 int CrasUtil::CrasGetAecGroupId() {
-  libcras_client* client = CrasConnect();
-  if (!client) {
-    return -1;
+  if (!cras_effects_cached_) {
+    cras_effects_cached_ = CacheEffects();
   }
-
-  int id;
-  int rc = libcras_client_get_aec_group_id(client, &id);
-  CrasDisconnect(&client);
-
-  return rc < 0 ? rc : id;
+  return aec_group_id_;
 }
 
 int CrasUtil::CrasGetDefaultOutputBufferSize() {
-  libcras_client* client = CrasConnect();
-  if (!client) {
-    return -1;
+  if (!cras_effects_cached_) {
+    cras_effects_cached_ = CacheEffects();
   }
-
-  int size;
-  int rc = libcras_client_get_default_output_buffer_size(client, &size);
-  CrasDisconnect(&client);
-
-  return rc < 0 ? rc : size;
+  return default_output_buffer_size_;
 }
 
 }  // namespace media
diff --git a/media/audio/cras/cras_util.h b/media/audio/cras/cras_util.h
index f76e1ce93..4b3ece7 100644
--- a/media/audio/cras/cras_util.h
+++ b/media/audio/cras/cras_util.h
@@ -48,17 +48,40 @@
   virtual ~CrasUtil();
 
   // Enumerates all devices of |type|.
+  // Virtual for testing.
   virtual std::vector<CrasDevice> CrasGetAudioDevices(DeviceType type);
 
   // Returns if system AEC is supported in CRAS.
+  // Virtual for testing.
   virtual int CrasGetAecSupported();
 
+  // Returns if system AGC is supported in CRAS.
+  // Virtual for testing.
+  virtual int CrasGetAgcSupported();
+
+  // Returns if system NS is supported in CRAS.
+  // Virtual for testing.
+  virtual int CrasGetNsSupported();
+
   // Returns the system AEC group ID. If no group ID is specified, -1 is
   // returned.
+  // Virtual for testing.
   virtual int CrasGetAecGroupId();
 
   // Returns the default output buffer size.
+  // Virtual for testing.
   virtual int CrasGetDefaultOutputBufferSize();
+
+ private:
+  int aec_supported_ = false;
+  int agc_supported_ = false;
+  int ns_supported_ = false;
+  int aec_group_id_ = -1;
+  int default_output_buffer_size_ = 0;
+  bool cras_effects_cached_ = false;
+
+  // Caches constant effect config from CRAS.
+  bool CacheEffects();
 };
 
 }  // namespace media
diff --git a/mojo/core/platform_wrapper_unittest.cc b/mojo/core/platform_wrapper_unittest.cc
index 72ee50c..7d65ffb 100644
--- a/mojo/core/platform_wrapper_unittest.cc
+++ b/mojo/core/platform_wrapper_unittest.cc
@@ -70,9 +70,7 @@
   base::FilePath temp_file_path;
   ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path));
   const std::string kMessage = "Hello, world!";
-  EXPECT_EQ(base::WriteFile(temp_file_path, kMessage.data(),
-                            static_cast<int>(kMessage.size())),
-            static_cast<int>(kMessage.size()));
+  ASSERT_TRUE(base::WriteFile(temp_file_path, kMessage));
 
   RunTestClient("ReadPlatformFile", [&](MojoHandle h) {
     // Open the temporary file for reading, wrap its handle, and send it to
diff --git a/mojo/public/rust/system/handle.rs b/mojo/public/rust/system/handle.rs
index 80e9d08b..8008a61 100644
--- a/mojo/public/rust/system/handle.rs
+++ b/mojo/public/rust/system/handle.rs
@@ -76,6 +76,7 @@
 /// `UntypedHandle` must hold either a valid `MojoHandle` or be
 /// `UntypedHandle::invalid()` (i.e. a 0 `MojoHandle`). The handle will be
 /// closed on `drop` if it is not `invalid()`.
+#[derive(Debug)]
 #[repr(transparent)]
 pub struct UntypedHandle {
     /// The native Mojo handle.
diff --git a/mojo/public/rust/system/shared_buffer.rs b/mojo/public/rust/system/shared_buffer.rs
index dabfb14..d0277f42 100644
--- a/mojo/public/rust/system/shared_buffer.rs
+++ b/mojo/public/rust/system/shared_buffer.rs
@@ -165,9 +165,8 @@
         }
     }
 
-    /// Retrieves information about a shared buffer the this handle. The return
-    /// value is a set of flags (a bit vector in a u32) representing different
-    /// aspects of the shared buffer and the size of the shared buffer.
+    /// Retrieves information about this shared buffer. The return value is just
+    /// the size of the shared buffer.
     pub fn get_info(&self) -> Result<u64, MojoResult> {
         let mut info = ffi::MojoSharedBufferInfo::new(0);
         let r = MojoResult::from_code(unsafe {
diff --git a/mojo/public/rust/tests/encoding.rs b/mojo/public/rust/tests/encoding.rs
index 24f44d4a..497e132b 100644
--- a/mojo/public/rust/tests/encoding.rs
+++ b/mojo/public/rust/tests/encoding.rs
@@ -39,43 +39,41 @@
 /// output, and decoding that once again.
 macro_rules! encoding_tests {
     ($($name:ident { MessageHeader => $header_cls:expr, $req_type:ident => $cls:expr } )*) => {
-        tests! {
-            $(
-            fn $name() {
-                let data = include_str!(concat!("../../interfaces/bindings/tests/data/validation/",
-                                                stringify!($name),
-                                                ".data"));
-                match util::parse_validation_test(data) {
-                    Ok((mut data, num_handles)) => {
-                        let mut mock_handles = Vec::with_capacity(num_handles);
-                        for _ in 0..num_handles {
-                            mock_handles.push(unsafe { system::acquire(0) });
-                        }
-                        println!("{}: Decoding header", stringify!($name));
-                        let header = MessageHeader::deserialize(&mut data[..], Vec::new()).expect("Should not error");
-                        let ctxt: Context = Default::default();
-                        let header_size = header.serialized_size(&ctxt);
-                        let header_cls = $header_cls;
-                        println!("{}: Verifying decoded header", stringify!($name));
-                        header_cls(header);
-                        let payload_buffer = &mut data[header_size..];
-                        let cls = $cls;
-                        println!("{}: Decoding payload", stringify!($name));
-                        let decoded_payload = $req_type::deserialize(payload_buffer, mock_handles).expect("Should not error");
-                        println!("{}: Verifying decoded payload", stringify!($name));
-                        cls(&decoded_payload);
-                        println!("{}: Re-encoding payload", stringify!($name));
-                        let (mut encoded_payload, handles) = decoded_payload.auto_serialize();
-                        println!("{}: Decoding payload again", stringify!($name));
-                        let redecoded_payload = $req_type::deserialize(&mut encoded_payload[..], handles).expect("Should not error");
-                        println!("{}: Verifying decoded payload again", stringify!($name));
-                        cls(&redecoded_payload);
-                    },
-                    Err(msg) => panic!("Error: {}", msg),
-                }
+        $(
+        mojo_test!($name, {
+            let data = include_str!(concat!("../../interfaces/bindings/tests/data/validation/",
+                                            stringify!($name),
+                                            ".data"));
+            match util::parse_validation_test(data) {
+                Ok((mut data, num_handles)) => {
+                    let mut mock_handles = Vec::with_capacity(num_handles);
+                    for _ in 0..num_handles {
+                        mock_handles.push(unsafe { system::acquire(0) });
+                    }
+                    println!("{}: Decoding header", stringify!($name));
+                    let header = MessageHeader::deserialize(&mut data[..], Vec::new()).expect("Should not error");
+                    let ctxt: Context = Default::default();
+                    let header_size = header.serialized_size(&ctxt);
+                    let header_cls = $header_cls;
+                    println!("{}: Verifying decoded header", stringify!($name));
+                    header_cls(header);
+                    let payload_buffer = &mut data[header_size..];
+                    let cls = $cls;
+                    println!("{}: Decoding payload", stringify!($name));
+                    let decoded_payload = $req_type::deserialize(payload_buffer, mock_handles).expect("Should not error");
+                    println!("{}: Verifying decoded payload", stringify!($name));
+                    cls(&decoded_payload);
+                    println!("{}: Re-encoding payload", stringify!($name));
+                    let (mut encoded_payload, handles) = decoded_payload.auto_serialize();
+                    println!("{}: Decoding payload again", stringify!($name));
+                    let redecoded_payload = $req_type::deserialize(&mut encoded_payload[..], handles).expect("Should not error");
+                    println!("{}: Verifying decoded payload again", stringify!($name));
+                    cls(&redecoded_payload);
+                },
+                Err(msg) => panic!("Error: {}", msg),
             }
-            )*
-        }
+        });
+        )*
     }
 }
 
diff --git a/mojo/public/rust/tests/integration.rs b/mojo/public/rust/tests/integration.rs
index 1e955e3..a934202 100644
--- a/mojo/public/rust/tests/integration.rs
+++ b/mojo/public/rust/tests/integration.rs
@@ -16,47 +16,46 @@
 
 use crate::util::mojom_validation::*;
 
-tests! {
-    // Tests basic client and server interaction over a thread
-    fn send_and_recv() {
-        let (endpt0, endpt1) = message_pipe::create().unwrap();
-        // Client and server handles
-        let client = IntegrationTestInterfaceClient::new(endpt0);
-        let server = IntegrationTestInterfaceServer::with_version(endpt1, 0);
+// Tests basic client and server interaction over a thread
+mojo_test!(send_and_recv, {
+    let (endpt0, endpt1) = message_pipe::create().unwrap();
+    // Client and server handles
+    let client = IntegrationTestInterfaceClient::new(endpt0);
+    let server = IntegrationTestInterfaceServer::with_version(endpt1, 0);
 
-        // Client thread
-        let handle = thread::spawn(move || {
-            // Send request
-            client.send_request(5, IntegrationTestInterfaceMethod0Request {
-                param0: BasicStruct {
-                    a: -1,
-                },
-            }).unwrap();
-            // Wait for response
-            client.pipe().wait(HandleSignals::READABLE);
-            // Decode response
-            let (req_id, options) = client.recv_response().unwrap();
-            assert_eq!(req_id, 5);
-            match options {
-                IntegrationTestInterfaceResponseOption::IntegrationTestInterfaceMethod0(msg) => {
-                    assert_eq!(msg.param0, vec![1, 2, 3]);
-                },
-            }
-        });
-        // Wait for request
-        server.pipe().wait(HandleSignals::READABLE);
-        // Decode request
-        let (req_id, options) = server.recv_response().unwrap();
+    // Client thread
+    let handle = thread::spawn(move || {
+        // Send request
+        client
+            .send_request(
+                5,
+                IntegrationTestInterfaceMethod0Request { param0: BasicStruct { a: -1 } },
+            )
+            .unwrap();
+        // Wait for response
+        client.pipe().wait(HandleSignals::READABLE);
+        // Decode response
+        let (req_id, options) = client.recv_response().unwrap();
         assert_eq!(req_id, 5);
         match options {
-            IntegrationTestInterfaceRequestOption::IntegrationTestInterfaceMethod0(msg) => {
-                assert_eq!(msg.param0.a, -1);
-            },
+            IntegrationTestInterfaceResponseOption::IntegrationTestInterfaceMethod0(msg) => {
+                assert_eq!(msg.param0, vec![1, 2, 3]);
+            }
         }
-        // Send response
-        server.send_request(5, IntegrationTestInterfaceMethod0Response {
-            param0: vec![1, 2, 3],
-        }).unwrap();
-        let _ = handle.join();
+    });
+    // Wait for request
+    server.pipe().wait(HandleSignals::READABLE);
+    // Decode request
+    let (req_id, options) = server.recv_response().unwrap();
+    assert_eq!(req_id, 5);
+    match options {
+        IntegrationTestInterfaceRequestOption::IntegrationTestInterfaceMethod0(msg) => {
+            assert_eq!(msg.param0.a, -1);
+        }
     }
-}
+    // Send response
+    server
+        .send_request(5, IntegrationTestInterfaceMethod0Response { param0: vec![1, 2, 3] })
+        .unwrap();
+    let _ = handle.join();
+});
diff --git a/mojo/public/rust/tests/lib.rs b/mojo/public/rust/tests/lib.rs
index aea7fcfd..63e016ff 100644
--- a/mojo/public/rust/tests/lib.rs
+++ b/mojo/public/rust/tests/lib.rs
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#![feature(assert_matches)]
+#![feature(maybe_uninit_write_slice)]
+
 #[macro_use]
 extern crate mojo;
 
diff --git a/mojo/public/rust/tests/regression.rs b/mojo/public/rust/tests/regression.rs
index 470eb9d..844efb2 100644
--- a/mojo/public/rust/tests/regression.rs
+++ b/mojo/public/rust/tests/regression.rs
@@ -57,40 +57,37 @@
 
 impl<T: MojomEncodable> MojomStruct for StructA<T> {}
 
-tests! {
-    // Fixed size arrays have complex and unsafe semantics to ensure
-    // there are no memory leaks. We test this behavior here to make
-    // sure memory isn't becoming corrupted.
-    fn regression_fixed_size_array_error_propagates_safely() {
-        let handle1 = unsafe { system::acquire(0) };
-        let handle2 = unsafe { system::acquire(0) };
-        let handle3 = unsafe { system::acquire(0) };
-        let val = StructA {
-            param0: [handle1, handle2, handle3],
-        };
-        let (mut buffer, mut handles) = val.auto_serialize();
-        handles.truncate(1);
-        let new_val = <StructA<UntypedHandle>>::deserialize(&mut buffer[..], handles);
-        match new_val {
-            Ok(_) => panic!("Value should not be okay!"),
-            Err(err) => assert_eq!(err, ValidationError::IllegalHandle),
-        }
+// Fixed size arrays have complex and unsafe semantics to ensure
+// there are no memory leaks. We test this behavior here to make
+// sure memory isn't becoming corrupted.
+mojo_test!(regression_fixed_size_array_error_propagates_safely, {
+    let handle1 = unsafe { system::acquire(0) };
+    let handle2 = unsafe { system::acquire(0) };
+    let handle3 = unsafe { system::acquire(0) };
+    let val = StructA { param0: [handle1, handle2, handle3] };
+    let (mut buffer, mut handles) = val.auto_serialize();
+    handles.truncate(1);
+    let new_val = <StructA<UntypedHandle>>::deserialize(&mut buffer[..], handles);
+    match new_val {
+        Ok(_) => panic!("Value should not be okay!"),
+        Err(err) => assert_eq!(err, ValidationError::IllegalHandle),
     }
+});
 
-    // Same as the above test, but verifies that drop() is called.
-    // For the only handle that should drop, we make the handle some
-    // random number which is potentially a valid handle. When on
-    // drop() we try to close it, we should panic.
-    #[should_panic]
+// Same as the above test, but verifies that drop() is called.
+// For the only handle that should drop, we make the handle some
+// random number which is potentially a valid handle. When on
+// drop() we try to close it, we should panic.
+mojo_test!(
+    regression_fixed_size_array_verify_drop,
     // Ignore this test, it panics while panicking
     #[ignore]
-    fn regression_fixed_size_array_verify_drop() {
+    #[should_panic]
+    {
         let handle1 = unsafe { system::acquire(42) };
         let handle2 = unsafe { system::acquire(0) };
         let handle3 = unsafe { system::acquire(0) };
-        let val = StructA {
-            param0: [handle1, handle2, handle3],
-        };
+        let val = StructA { param0: [handle1, handle2, handle3] };
         let (mut buffer, mut handles) = val.auto_serialize();
         handles.truncate(1);
         let new_val = <StructA<UntypedHandle>>::deserialize(&mut buffer[..], handles);
@@ -99,4 +96,4 @@
             Err(err) => assert_eq!(err, ValidationError::IllegalHandle),
         }
     }
-}
+);
diff --git a/mojo/public/rust/tests/run_loop.rs b/mojo/public/rust/tests/run_loop.rs
index eaa4a99d..9a82bf0c 100644
--- a/mojo/public/rust/tests/run_loop.rs
+++ b/mojo/public/rust/tests/run_loop.rs
@@ -243,209 +243,263 @@
     }
 }
 
-tests! {
-    // Verifies that after adding and removing, we can run, exit and be
-    // left in a consistent state.
-    fn add_remove() {
-        run_loop::with_current(|runloop| {
-            let (endpt0, endpt1) = message_pipe::create().unwrap();
-            let token0 = runloop.register(&endpt0, HandleSignals::WRITABLE, 0, HandlerExpectReady {});
-            let token1 = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, HandlerExpectReady {});
-            runloop.deregister(token1);
-            runloop.deregister(token0);
-            runloop.run();
-        })
-    }
+// Verifies that after adding and removing, we can run, exit and be left in a
+// consistent state.
+mojo_test!(add_remove, {
+    run_loop::with_current(|runloop| {
+        let (endpt0, endpt1) = message_pipe::create().unwrap();
+        let token0 = runloop.register(&endpt0, HandleSignals::WRITABLE, 0, HandlerExpectReady {});
+        let token1 = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, HandlerExpectReady {});
+        runloop.deregister(token1);
+        runloop.deregister(token0);
+        runloop.run();
+    })
+});
 
-    // Verifies that generated tokens are unique.
-    fn tokens() {
-        let mut vec = Vec::new();
-        run_loop::with_current(|runloop| {
-            for _ in 0..10 {
-                let (_endpt0, endpt1) = message_pipe::create().unwrap();
-                vec.push(runloop.register(&endpt1, HandleSignals::empty(), 0, HandlerExpectReady {}));
-            }
-            for i in 0..10 {
-                for j in 0..10 {
-                    if i != j {
-                        assert!(vec[i] != vec[j]);
-                    }
+// Verifies that generated tokens are unique.
+mojo_test!(tokens, {
+    let mut vec = Vec::new();
+    run_loop::with_current(|runloop| {
+        for _ in 0..10 {
+            let (_endpt0, endpt1) = message_pipe::create().unwrap();
+            vec.push(runloop.register(&endpt1, HandleSignals::empty(), 0, HandlerExpectReady {}));
+        }
+        for i in 0..10 {
+            for j in 0..10 {
+                if i != j {
+                    assert!(vec[i] != vec[j]);
                 }
             }
-        });
-    }
+        }
+    });
+});
 
-    // Verifies that the handler's "on_ready" function is called.
-    fn notify_results() {
-        let (_endpt0, endpt1) = message_pipe::create().unwrap();
-        run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, MOJO_INDEFINITE, HandlerExpectReady {});
-            runloop.run();
-        });
-    }
+// Verifies that the handler's "on_ready" function is called.
+mojo_test!(notify_results, {
+    let (_endpt0, endpt1) = message_pipe::create().unwrap();
+    run_loop::with_current(|runloop| {
+        let _ = runloop.register(
+            &endpt1,
+            HandleSignals::WRITABLE,
+            MOJO_INDEFINITE,
+            HandlerExpectReady {},
+        );
+        runloop.run();
+    });
+});
 
-    // Verifies that the handler's "on_error" function is called.
-    fn notify_error() {
-        // Drop the first endpoint immediately
-        let (_, endpt1) = message_pipe::create().unwrap();
-        run_loop::with_current(|runloop| {
-            let was_called = Rc::new(Cell::new(false));
-            let _ = runloop.register(&endpt1, HandleSignals::READABLE, 0,
-                HandlerExpectError {
-                    was_called: was_called.clone(),
-            });
-            runloop.run();
-            assert!(was_called.get(), "on_error was not called");
-        });
-    }
+// Verifies that the handler's "on_error" function is called.
+mojo_test!(notify_error, {
+    // Drop the first endpoint immediately
+    let (_, endpt1) = message_pipe::create().unwrap();
+    run_loop::with_current(|runloop| {
+        let was_called = Rc::new(Cell::new(false));
+        let _ = runloop.register(
+            &endpt1,
+            HandleSignals::READABLE,
+            0,
+            HandlerExpectError { was_called: was_called.clone() },
+        );
+        runloop.run();
+        assert!(was_called.get(), "on_error was not called");
+    });
+});
 
-    // Verifies that the handler's "on_ready" function is called which only
-    // quits.
-    fn notify_ready_quit() {
-        let (_endpt0, endpt1) = message_pipe::create().unwrap();
-        run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, MOJO_INDEFINITE, HandlerQuit {});
-            runloop.run();
-        });
-    }
+// Verifies that the handler's "on_ready" function is called which only quits.
+mojo_test!(notify_ready_quit, {
+    let (_endpt0, endpt1) = message_pipe::create().unwrap();
+    run_loop::with_current(|runloop| {
+        let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, MOJO_INDEFINITE, HandlerQuit {});
+        runloop.run();
+    });
+});
 
-    // Tests more complex behavior, i.e. the interaction between two handlers.
-    fn register_deregister() {
-        let (_endpt0, endpt1) = message_pipe::create().unwrap();
-        run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, MOJO_INDEFINITE, HandlerRegister {});
-            runloop.run();
-        });
-    }
+// Tests more complex behavior, i.e. the interaction between two handlers.
+mojo_test!(register_deregister, {
+    let (_endpt0, endpt1) = message_pipe::create().unwrap();
+    run_loop::with_current(|runloop| {
+        let _ =
+            runloop.register(&endpt1, HandleSignals::WRITABLE, MOJO_INDEFINITE, HandlerRegister {});
+        runloop.run();
+    });
+});
 
-    // Tests reregistering.
+// Tests reregistering.
+mojo_test!(
+    reregister,
     #[ignore]
-    fn reregister() {
+    {
         let (_endpt0, endpt1) = message_pipe::create().unwrap();
         run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::READABLE, 0, HandlerReregister { count: 0 });
+            let _ = runloop.register(
+                &endpt1,
+                HandleSignals::READABLE,
+                0,
+                HandlerReregister { count: 0 },
+            );
             runloop.run();
         });
     }
+);
 
-    // Tests nesting run loops by having a handler create a new one.
+// Tests nesting run loops by having a handler create a new one.
+mojo_test!(
+    nesting,
     #[ignore]
-    fn nesting() {
+    {
         let (_endpt0, endpt1) = message_pipe::create().unwrap();
         run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::READABLE, 0, HandlerNesting { count: 0 });
+            let _ =
+                runloop.register(&endpt1, HandleSignals::READABLE, 0, HandlerNesting { count: 0 });
             runloop.run();
         });
     }
+);
 
-    // Tests to make sure nesting with the SAME runloop fails.
+// Tests to make sure nesting with the SAME runloop fails.
+mojo_test!(
+    bad_nesting,
     #[should_panic]
     #[ignore]
-    fn bad_nesting() {
+    {
         let (_endpt0, endpt1) = message_pipe::create().unwrap();
         run_loop::with_current(|runloop| {
             let _ = runloop.register(&endpt1, HandleSignals::READABLE, 0, HandlerBadNesting {});
             runloop.run();
         });
     }
+);
 
-    // Tests adding a simple task that adds a handler.
-    fn simple_task() {
-        run_loop::with_current(|runloop| {
-            let was_called = Rc::new(Cell::new(false));
-            // The inner closure cannot take `was_called` by reference since we
-            // cannot prove it lives long enough to the borrow checker. It must
-            // be cloned first.
-            let was_called_clone = was_called.clone();
-            let (_, endpt1) = message_pipe::create().unwrap();
-            // If `endpt1` is moved into the task closure, it is dropped at the
-            // end of its call. This means we won't get the signal we're looking
-            // for: we'll just get a notification that the handle was closed.
-            // Make it live longer by wrapping it in an Rc.
-            let endpt1 = Rc::new(endpt1);
-            let endpt1_clone = endpt1.clone();
-            let _ = runloop.post_task(move |runloop: &mut RunLoop| {
-                let _ = runloop.register(&*endpt1_clone, HandleSignals::READABLE, 0,
-                    HandlerExpectError {
-                        was_called: was_called_clone,
-                    });
-            }, 0).unwrap();
-            runloop.run();
+// Tests adding a simple task that adds a handler.
+mojo_test!(simple_task, {
+    run_loop::with_current(|runloop| {
+        let was_called = Rc::new(Cell::new(false));
+        // The inner closure cannot take `was_called` by reference since we
+        // cannot prove it lives long enough to the borrow checker. It must be
+        // cloned first.
+        let was_called_clone = was_called.clone();
+        let (_, endpt1) = message_pipe::create().unwrap();
+        // If `endpt1` is moved into the task closure, it is dropped at the
+        // end of its call. This means we won't get the signal we're looking
+        // for: we'll just get a notification that the handle was closed.
+        // Make it live longer by wrapping it in an Rc.
+        let endpt1 = Rc::new(endpt1);
+        let endpt1_clone = endpt1.clone();
+        let _ = runloop
+            .post_task(
+                move |runloop: &mut RunLoop| {
+                    let _ = runloop.register(
+                        &*endpt1_clone,
+                        HandleSignals::READABLE,
+                        0,
+                        HandlerExpectError { was_called: was_called_clone },
+                    );
+                },
+                0,
+            )
+            .unwrap();
+        runloop.run();
 
-            // Ensure we got the `on_error` call for the unsatisfiable signal.
-            assert!(was_called.get(), "on_error was not called");
-        });
-    }
+        // Ensure we got the `on_error` call for the unsatisfiable signal.
+        assert!(was_called.get(), "on_error was not called");
+    });
+});
 
-    // Tests using a handler that adds a bunch of tasks.
-    fn handler_tasks() {
-        let (_endpt0, endpt1) = message_pipe::create().unwrap();
-        let r = Rc::new(Cell::new(0));
-        run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, HandlerTasks { count: r.clone() });
-            runloop.run();
-            assert!((*r).get() >= 11);
-        });
-    }
+// Tests using a handler that adds a bunch of tasks.
+mojo_test!(handler_tasks, {
+    let (_endpt0, endpt1) = message_pipe::create().unwrap();
+    let r = Rc::new(Cell::new(0));
+    run_loop::with_current(|runloop| {
+        let _ = runloop.register(
+            &endpt1,
+            HandleSignals::WRITABLE,
+            0,
+            HandlerTasks { count: r.clone() },
+        );
+        runloop.run();
+        assert!((*r).get() >= 11);
+    });
+});
 
-    // Tests using a handler that adds a bunch of tasks.
-    fn nested_tasks() {
-        let (_endpt0, endpt1) = message_pipe::create().unwrap();
-        let r = Rc::new(Cell::new(0));
-        run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, NestedTasks { count: r.clone(), quitter: false });
-            runloop.run();
-            assert!((*r).get() >= 10);
-        });
-    }
+// Tests using a handler that adds a bunch of tasks.
+mojo_test!(nested_tasks, {
+    let (_endpt0, endpt1) = message_pipe::create().unwrap();
+    let r = Rc::new(Cell::new(0));
+    run_loop::with_current(|runloop| {
+        let _ = runloop.register(
+            &endpt1,
+            HandleSignals::WRITABLE,
+            0,
+            NestedTasks { count: r.clone(), quitter: false },
+        );
+        runloop.run();
+        assert!((*r).get() >= 10);
+    });
+});
 
-    // Tests using a handler that adds a bunch of tasks.
-    //
-    // This test is disabled because it posts tasks which call `RunLoop::quit`,
-    // some of which get left on the `RunLoop` after the test ends. These
-    // interfere with later tests on the same thread.
+// Tests using a handler that adds a bunch of tasks.
+//
+// This test is disabled because it posts tasks which call `RunLoop::quit`, some
+// of which get left on the `RunLoop` after the test ends. These interfere with
+// later tests on the same thread.
+mojo_test!(
+    nested_tasks_quit,
     #[ignore]
-    fn nested_tasks_quit() {
+    {
         let (_endpt0, endpt1) = message_pipe::create().unwrap();
         let r = Rc::new(Cell::new(0));
         run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, NestedTasks { count: r.clone(), quitter: true });
+            let _ = runloop.register(
+                &endpt1,
+                HandleSignals::WRITABLE,
+                0,
+                NestedTasks { count: r.clone(), quitter: true },
+            );
             runloop.run();
             assert!((*r).get() >= 10);
         });
     }
+);
 
-    fn close_handle() {
-        let (_endpt0, endpt1) = message_pipe::create().unwrap();
-        run_loop::with_current(|runloop| {
-            let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, HandlerQuit {});
-            drop(endpt1);
-            runloop.run();
-        })
-    }
+mojo_test!(close_handle, {
+    let (_endpt0, endpt1) = message_pipe::create().unwrap();
+    run_loop::with_current(|runloop| {
+        let _ = runloop.register(&endpt1, HandleSignals::WRITABLE, 0, HandlerQuit {});
+        drop(endpt1);
+        runloop.run();
+    })
+});
 
-    // Tests that `RunLoop::run` will run posted tasks even if there's no
-    // handles to wait on.
-    fn post_tasks_without_handles() {
-        let outer_called = Rc::new(Cell::new(false));
-        let outer_called_clone = outer_called.clone();
-        let inner_called = Rc::new(Cell::new(false));
-        let inner_called_clone = inner_called.clone();
+// Tests that `RunLoop::run` will run posted tasks even if there's no handles to
+// wait on.
+mojo_test!(post_tasks_without_handles, {
+    let outer_called = Rc::new(Cell::new(false));
+    let outer_called_clone = outer_called.clone();
+    let inner_called = Rc::new(Cell::new(false));
+    let inner_called_clone = inner_called.clone();
 
-        run_loop::with_current(move |runloop| {
-            // Post a task, and then post another task from within the first.
-            // This is to check RunLoop will run all the tasks it can before
-            // quitting.
-            runloop.post_task(move |runloop| {
-                outer_called_clone.set(true);
-                runloop.post_task(move |_| {
-                    inner_called_clone.set(true);
-                }, 0).unwrap();
-            }, 0).unwrap();
-            runloop.run();
-        });
+    run_loop::with_current(move |runloop| {
+        // Post a task, and then post another task from within the first. This
+        // is to check RunLoop will run all the tasks it can before quitting.
+        runloop
+            .post_task(
+                move |runloop| {
+                    outer_called_clone.set(true);
+                    runloop
+                        .post_task(
+                            move |_| {
+                                inner_called_clone.set(true);
+                            },
+                            0,
+                        )
+                        .unwrap();
+                },
+                0,
+            )
+            .unwrap();
+        runloop.run();
+    });
 
-        assert!(outer_called.get());
-        assert!(inner_called.get());
-    }
-}
+    assert!(outer_called.get());
+    assert!(inner_called.get());
+});
diff --git a/mojo/public/rust/tests/system.rs b/mojo/public/rust/tests/system.rs
index 739e7fe..db680cb4 100644
--- a/mojo/public/rust/tests/system.rs
+++ b/mojo/public/rust/tests/system.rs
@@ -8,7 +8,6 @@
 //! and the result being caught in the test! macro. If a test function
 //! returns without panicking, it is assumed to pass.
 
-use mojo::system::core;
 use mojo::system::data_pipe;
 use mojo::system::message_pipe;
 use mojo::system::shared_buffer::{self, SharedBuffer};
@@ -18,405 +17,400 @@
 use mojo::system::wait_set;
 use mojo::system::{self, CastHandle, Handle, HandleSignals, MojoResult, SignalsState};
 
+use std::assert_matches::assert_matches;
+use std::mem::drop;
 use std::string::String;
 use std::sync::{Arc, Condvar, Mutex};
 use std::thread;
 use std::vec::Vec;
 
-tests! {
-    fn get_time_ticks_now() {
-        let x = core::get_time_ticks_now();
-        assert!(x >= 10);
+mojo_test!(handle, {
+    let sb = SharedBuffer::new(1).unwrap();
+    let handle = sb.as_untyped();
+    unsafe {
+        assert_ne!(handle.get_native_handle(), 0);
+        assert!(handle.is_valid());
+        let mut h2 = system::acquire(handle.get_native_handle());
+        assert!(h2.is_valid());
+        h2.invalidate();
+        assert!(!h2.is_valid());
+    }
+});
+
+mojo_test!(shared_buffer, {
+    let bufsize = 100;
+
+    // Create a shared buffer and test round trip through `UntypedHandle`.
+    let sb_first = SharedBuffer::new(bufsize).unwrap();
+    // Get original native handle to check against.
+    let sb_native_handle = sb_first.get_native_handle();
+
+    let sb_untyped = sb_first.as_untyped();
+    assert_eq!(sb_untyped.get_native_handle(), sb_native_handle);
+    let sb = unsafe { SharedBuffer::from_untyped(sb_untyped) };
+    assert_eq!(sb.get_native_handle(), sb_native_handle);
+
+    // Check the reported size is the same as our requested size.
+    let size = sb.get_info().unwrap();
+    assert_eq!(size, bufsize);
+
+    // Map the buffer.
+    let mut buf = sb.map(0, bufsize).unwrap();
+    assert_eq!(buf.len(), bufsize as usize);
+    buf.write(50, 34);
+
+    // Duplicate it and drop the original handle, which should maintain the
+    // `buf` mapping.
+    let sb1 = sb.duplicate(shared_buffer::DuplicateFlags::empty()).unwrap();
+    drop(sb);
+
+    buf.write(51, 35);
+
+    // Unmap `buf` by dropping it.
+    drop(buf);
+
+    // Create a new mapping and check for what we wrote.
+    let buf1 = sb1.map(50, 50).unwrap();
+    assert_eq!(buf1.len(), 50);
+    // verify buffer contents
+    assert_eq!(buf1.read(0), 34);
+    assert_eq!(buf1.read(1), 35);
+});
+
+mojo_test!(message_pipe, {
+    let (end_a, end_b) = message_pipe::create().unwrap();
+
+    // Extract original handle to check against.
+    let end_a_native_handle = end_a.get_native_handle();
+    // Test casting of handle types.
+    let end_a_untyped = end_a.as_untyped();
+    assert_eq!(end_a_untyped.get_native_handle(), end_a_native_handle);
+
+    // Test after UntypedHandle round trip.
+    let end_a = unsafe { message_pipe::MessageEndpoint::from_untyped(end_a_untyped) };
+    assert_eq!(end_a.get_native_handle(), end_a_native_handle);
+    let s: SignalsState = end_a.wait(HandleSignals::WRITABLE).satisfied().unwrap();
+    assert!(s.satisfied().is_writable());
+    assert!(s.satisfiable().is_readable());
+    assert!(s.satisfiable().is_writable());
+    assert!(s.satisfiable().is_peer_closed());
+
+    assert_matches!(end_a.read(), Err(mojo::MojoResult::ShouldWait));
+    let hello = "hello".to_string().into_bytes();
+    let write_result = end_b.write(&hello, Vec::new());
+    assert_eq!(write_result, mojo::MojoResult::Okay);
+    let s: SignalsState = end_a.wait(HandleSignals::READABLE).satisfied().unwrap();
+    assert!(s.satisfied().is_readable());
+    assert!(s.satisfied().is_writable());
+    assert!(s.satisfiable().is_readable());
+    assert!(s.satisfiable().is_writable());
+    assert!(s.satisfiable().is_peer_closed());
+
+    let (hello_data, _handles) = end_a.read().expect("failed to read from end_a");
+    assert_eq!(String::from_utf8(hello_data), Ok("hello".to_string()));
+
+    // Closing one endpoint should be seen by the other.
+    drop(end_a);
+
+    let s: SignalsState =
+        end_b.wait(HandleSignals::READABLE | HandleSignals::WRITABLE).unsatisfiable().unwrap();
+    assert!(s.satisfied().is_peer_closed());
+    // For some reason QuotaExceeded is also set. TOOD(collinbaker): investigate.
+    assert!(s.satisfiable().is_peer_closed());
+});
+
+mojo_test!(data_pipe, {
+    let (consumer, producer) = data_pipe::create_default().unwrap();
+    // Extract original handle to check against
+    let consumer_native_handle = consumer.get_native_handle();
+    let producer_native_handle = producer.get_native_handle();
+    // Test casting of handle types
+    let consumer_untyped = consumer.as_untyped();
+    let producer_untyped = producer.as_untyped();
+    assert_eq!(consumer_untyped.get_native_handle(), consumer_native_handle);
+    assert_eq!(producer_untyped.get_native_handle(), producer_native_handle);
+    let consumer = unsafe { data_pipe::Consumer::<u8>::from_untyped(consumer_untyped) };
+    let producer = unsafe { data_pipe::Producer::<u8>::from_untyped(producer_untyped) };
+    assert_eq!(consumer.get_native_handle(), consumer_native_handle);
+    assert_eq!(producer.get_native_handle(), producer_native_handle);
+
+    // Ensure the producer is writable, and check that we can wait on this
+    // (which should return immediately).
+    producer.wait(HandleSignals::WRITABLE).satisfied().unwrap();
+
+    // Try writing a message.
+    let hello = "hello".to_string().into_bytes();
+    let bytes_written = producer.write(&hello, data_pipe::WriteFlags::empty()).unwrap();
+    assert_eq!(bytes_written, hello.len());
+
+    // Try reading our message.
+    consumer.wait(HandleSignals::READABLE).satisfied().unwrap();
+    let data_string =
+        String::from_utf8(consumer.read(data_pipe::ReadFlags::empty()).unwrap()).unwrap();
+    assert_eq!(data_string, "hello".to_string());
+
+    // Test two-phase read/write, where we acquire a buffer to use then
+    // commit the read/write when done.
+    let goodbye = "goodbye".to_string().into_bytes();
+    let mut write_buf = producer.begin().expect("error on write begin");
+    assert!(write_buf.len() >= goodbye.len());
+    std::mem::MaybeUninit::write_slice(&mut write_buf[0..goodbye.len()], &goodbye);
+    // SAFETY: we wrote `goodbye.len()` valid elements to `write_buf`,
+    // so they are initialized.
+    unsafe {
+        write_buf.commit(goodbye.len());
     }
 
-    fn handle() {
-        let sb = SharedBuffer::new(1).unwrap();
-        let handle = sb.as_untyped();
-        unsafe {
-            assert_eq!((handle.get_native_handle() != 0), handle.is_valid());
-            assert!(handle.get_native_handle() != 0 && handle.is_valid());
-            let mut h2 = system::acquire(handle.get_native_handle());
-            assert!(h2.is_valid());
-            h2.invalidate();
-            assert!(!h2.is_valid());
-        }
-    }
+    // Try a two-phase read and check that we get the same result.
+    consumer.wait(HandleSignals::READABLE).satisfied().unwrap();
+    let read_buf = consumer.begin().expect("error on read begin");
 
-    fn shared_buffer() {
-        let bufsize = 100;
-        let sb1;
-        {
-            let mut buf;
-            {
-                let sb_c = SharedBuffer::new(bufsize).unwrap();
-                // Extract original handle to check against
-                let sb_h = sb_c.get_native_handle();
-                // Test casting of handle types
-                let sb_u = sb_c.as_untyped();
-                assert_eq!(sb_u.get_native_handle(), sb_h);
-                let sb = unsafe { SharedBuffer::from_untyped(sb_u) };
-                assert_eq!(sb.get_native_handle(), sb_h);
-                // Test map
-                buf = sb.map(0, bufsize).unwrap();
-                assert_eq!(buf.len(), bufsize as usize);
-                // Test get info
-                let size = sb.get_info().unwrap();
-                assert_eq!(size, bufsize);
-                buf.write(50, 34);
-                // Test duplicate
-                sb1 = sb.duplicate(shared_buffer::DuplicateFlags::empty()).unwrap();
-            }
-            // sb gets closed
-            buf.write(51, 35);
-        }
-        // buf just got closed
-        // remap to buf1 from sb1
-        let buf1 = sb1.map(50, 50).unwrap();
-        assert_eq!(buf1.len(), 50);
-        // verify buffer contents
-        assert_eq!(buf1.read(0), 34);
-        assert_eq!(buf1.read(1), 35);
-    }
+    // Ensure we get an error when attempting another read.
+    assert_matches!(consumer.read(data_pipe::ReadFlags::empty()), Err(mojo::MojoResult::Busy));
 
-    fn message_pipe() {
-        let (endpt, endpt1) = message_pipe::create().unwrap();
-        // Extract original handle to check against
-        let endpt_h = endpt.get_native_handle();
-        // Test casting of handle types
-        let endpt_u = endpt.as_untyped();
-        assert_eq!(endpt_u.get_native_handle(), endpt_h);
-        {
-            let endpt0 = unsafe { message_pipe::MessageEndpoint::from_untyped(endpt_u) };
-            assert_eq!(endpt0.get_native_handle(), endpt_h);
-            {
-                let s: SignalsState = endpt0.wait(HandleSignals::WRITABLE).satisfied().unwrap();
-                assert!(s.satisfied().is_writable());
-                assert!(s.satisfiable().is_readable());
-                assert!(s.satisfiable().is_writable());
-                assert!(s.satisfiable().is_peer_closed());
-            }
-            match endpt0.read() {
-                Ok((_msg, _handles)) => panic!("Read should not have succeeded."),
-                Err(r) => assert_eq!(r, mojo::MojoResult::ShouldWait),
-            }
-            let hello = "hello".to_string().into_bytes();
-            let write_result = endpt1.write(&hello, Vec::new());
-            assert_eq!(write_result, mojo::MojoResult::Okay);
-            {
-                let s: SignalsState = endpt0.wait(HandleSignals::READABLE).satisfied().unwrap();
-                assert!(s.satisfied().is_readable());
-                assert!(s.satisfied().is_writable());
-                assert!(s.satisfiable().is_readable());
-                assert!(s.satisfiable().is_writable());
-                assert!(s.satisfiable().is_peer_closed());
-            }
-            let hello_data;
-            match endpt0.read() {
-                Ok((msg, _handles)) => hello_data = msg,
-                Err(r) => panic!("Failed to read message on endpt0, error: {}", r),
-            }
-            assert_eq!(String::from_utf8(hello_data).unwrap(), "hello".to_string());
-        }
-        let s: SignalsState = endpt1.wait(HandleSignals::READABLE | HandleSignals::WRITABLE).unsatisfiable().unwrap();
-        assert!(s.satisfied().is_peer_closed());
-        // For some reason QuotaExceeded is also set. TOOD(collinbaker): investigate.
-        assert!(s.satisfiable().is_peer_closed());
-    }
+    // Copy the buffer to ensure we commit the read before asserting.
+    let data = read_buf.to_vec();
+    read_buf.commit(data.len());
 
-    fn data_pipe() {
-        let (cons0, prod0) = data_pipe::create_default().unwrap();
-        // Extract original handle to check against
-        let cons_h = cons0.get_native_handle();
-        let prod_h = prod0.get_native_handle();
-        // Test casting of handle types
-        let cons_u = cons0.as_untyped();
-        let prod_u = prod0.as_untyped();
-        assert_eq!(cons_u.get_native_handle(), cons_h);
-        assert_eq!(prod_u.get_native_handle(), prod_h);
-        let cons = unsafe { data_pipe::Consumer::<u8>::from_untyped(cons_u) };
-        let prod = unsafe { data_pipe::Producer::<u8>::from_untyped(prod_u) };
-        assert_eq!(cons.get_native_handle(), cons_h);
-        assert_eq!(prod.get_native_handle(), prod_h);
-        // Test waiting on producer
-        prod.wait(HandleSignals::WRITABLE).satisfied().unwrap();
-        // Test one-phase read/write.
-        // Writing.
+    assert_eq!(data, goodbye);
+});
+
+mojo_test!(wait_set, {
+    let mut set = wait_set::WaitSet::new().unwrap();
+    let (endpt0, endpt1) = message_pipe::create().unwrap();
+    let cookie1 = wait_set::WaitSetCookie(245);
+    let cookie2 = wait_set::WaitSetCookie(123);
+    let signals = HandleSignals::READABLE;
+    assert_eq!(set.add(&endpt0, signals, cookie1), mojo::MojoResult::Okay);
+    assert_eq!(set.add(&endpt0, signals, cookie1), mojo::MojoResult::AlreadyExists);
+    assert_eq!(set.remove(cookie1), mojo::MojoResult::Okay);
+    assert_eq!(set.remove(cookie1), mojo::MojoResult::NotFound);
+    assert_eq!(set.add(&endpt0, signals, cookie2), mojo::MojoResult::Okay);
+    thread::spawn(move || {
         let hello = "hello".to_string().into_bytes();
-        let bytes_written = prod.write(&hello, data_pipe::WriteFlags::empty()).unwrap();
-        assert_eq!(bytes_written, hello.len());
-        // Reading.
-        cons.wait(HandleSignals::READABLE).satisfied().unwrap();
-        let data_string = String::from_utf8(cons.read(data_pipe::ReadFlags::empty()).unwrap()).unwrap();
-        assert_eq!(data_string, "hello".to_string());
-        {
-            // Test two-phase read/write.
-            // Writing.
-            let goodbye = "goodbye".to_string().into_bytes();
-            let mut write_buf = match prod.begin() {
-                Ok(buf) => buf,
-                Err(err) => panic!("Error on write begin: {}", err),
-            };
-            assert!(write_buf.len() >= goodbye.len());
-            for i in 0..goodbye.len() {
-                write_buf[i].write(goodbye[i]);
-            }
-            // SAFETY: we wrote `goodbye.len()` valid elements to `write_buf`,
-            // so they are initialized.
-            unsafe {
-                write_buf.commit(goodbye.len());
-            }
-            // Reading.
-            cons.wait(HandleSignals::READABLE).satisfied().unwrap();
-            let mut data_goodbye: Vec<u8> = Vec::with_capacity(goodbye.len());
-            {
-                let read_buf = match cons.begin() {
-                    Ok(buf) => buf,
-                    Err(err) => panic!("Error on read begin: {}", err),
-                };
-                for i in 0..read_buf.len() {
-                    data_goodbye.push(read_buf[i]);
-                }
-                match cons.read(data_pipe::ReadFlags::empty()) {
-                    Ok(_bytes) => assert!(false),
-                    Err(r) => assert_eq!(r, mojo::MojoResult::Busy),
-                }
-                read_buf.commit(data_goodbye.len())
-            }
-            assert_eq!(data_goodbye.len(), goodbye.len());
-            assert_eq!(String::from_utf8(data_goodbye).unwrap(), "goodbye".to_string());
-        }
-    }
+        let write_result = endpt1.write(&hello, Vec::new());
+        assert_eq!(write_result, mojo::MojoResult::Okay);
+    });
+    let mut output = Vec::with_capacity(2);
+    let result = set.wait_on_set(&mut output);
+    assert_eq!(result, mojo::MojoResult::Okay);
+    assert_eq!(output.len(), 1);
+    assert_eq!(output[0].cookie, cookie2);
+    assert_eq!(output[0].wait_result, mojo::MojoResult::Okay);
+    assert!(output[0].signals_state.satisfied().is_readable());
+});
 
-    fn wait_set() {
-        let mut set = wait_set::WaitSet::new().unwrap();
-        let (endpt0, endpt1) = message_pipe::create().unwrap();
-        let cookie1 = wait_set::WaitSetCookie(245);
-        let cookie2 = wait_set::WaitSetCookie(123);
-        let signals = HandleSignals::READABLE;
-        assert_eq!(set.add(&endpt0, signals, cookie1), mojo::MojoResult::Okay);
-        assert_eq!(set.add(&endpt0, signals, cookie1), mojo::MojoResult::AlreadyExists);
-        assert_eq!(set.remove(cookie1), mojo::MojoResult::Okay);
-        assert_eq!(set.remove(cookie1), mojo::MojoResult::NotFound);
-        assert_eq!(set.add(&endpt0, signals, cookie2), mojo::MojoResult::Okay);
-        thread::spawn(move || {
-            let hello = "hello".to_string().into_bytes();
-            let write_result = endpt1.write(&hello, Vec::new());
-            assert_eq!(write_result, mojo::MojoResult::Okay);
-        });
-        let mut output = Vec::with_capacity(2);
-        let result = set.wait_on_set(&mut output);
-        assert_eq!(result, mojo::MojoResult::Okay);
-        assert_eq!(output.len(), 1);
-        assert_eq!(output[0].cookie, cookie2);
-        assert_eq!(output[0].wait_result, mojo::MojoResult::Okay);
-        assert!(output[0].signals_state.satisfied().is_readable());
-    }
+mojo_test!(trap_signals_on_readable, {
+    // These tests unfortunately need global state, so we have to ensure
+    // exclusive access (generally Rust tests run on multiple threads).
+    let _test_lock = TRAP_TEST_LOCK.lock().unwrap();
 
-    fn trap_signals_on_readable() {
-        // These tests unfortunately need global state, so we have to ensure
-        // exclusive access (generally Rust tests run on multiple threads).
-        let _test_lock = TRAP_TEST_LOCK.lock().unwrap();
+    let trap = UnsafeTrap::new(test_trap_event_handler).unwrap();
 
-        let trap = UnsafeTrap::new(test_trap_event_handler).unwrap();
-
-        let (cons, prod) = data_pipe::create_default().unwrap();
-        assert_eq!(MojoResult::Okay,
-            trap.add_trigger(cons.get_native_handle(),
-                             HandleSignals::READABLE,
-                             TriggerCondition::SignalsSatisfied,
-                             1));
-        assert_eq!(MojoResult::Okay,
-            trap.add_trigger(prod.get_native_handle(),
-                             HandleSignals::PEER_CLOSED,
-                             TriggerCondition::SignalsSatisfied,
-                             2));
-
-        let mut blocking_events_buf = [std::mem::MaybeUninit::uninit(); 16];
-        // The trap should arm with no blocking events since nothing should be
-        // triggered yet.
-        match trap.arm(Some(&mut blocking_events_buf)) {
-            ArmResult::Armed => (),
-            ArmResult::Blocked(events) => panic!("unexpected blocking events {:?}", events),
-            ArmResult::Failed(e) => panic!("unexpected mojo error {:?}", e),
-        }
-
-        // Check that there are no events in the list (though of course this
-        // check is uncertain if a race condition bug exists).
-        assert_eq!(TRAP_EVENT_LIST.lock().unwrap().len(), 0);
-
-        // Write to `prod` making `cons` readable.
-        assert_eq!(prod.write(&[128u8], data_pipe::WriteFlags::empty()).unwrap(), 1);
-        {
-            let list = wait_for_trap_events(TRAP_EVENT_LIST.lock().unwrap(), 1);
-            assert_eq!(list.len(), 1);
-            let event = list[0];
-            assert_eq!(event.trigger_context(), 1);
-            assert_eq!(event.result(), MojoResult::Okay);
-            assert!(event.signals_state().satisfiable().is_readable(),
-                    "{:?}", event.signals_state());
-            assert!(event.signals_state().satisfied().is_readable(),
-                    "{:?}", event.signals_state());
-        }
-
-        // Once the above event has fired, `trap` is disarmed.
-
-        // Re-arming should block and return the event above.
-        match trap.arm(Some(&mut blocking_events_buf)) {
-            ArmResult::Blocked(events) => {
-                let event: &UnsafeTrapEvent = events.get(0).unwrap();
-                assert_eq!(event.trigger_context(), 1);
-                assert_eq!(event.result(), MojoResult::Okay);
-            }
-            ArmResult::Armed => panic!("expected event did not arrive"),
-            ArmResult::Failed(e) => panic!("unexpected Mojo error {:?}", e),
-        }
-
-        clear_trap_events(1);
-
-        // Read the data so we don't receive the same event again.
-        cons.read(data_pipe::ReadFlags::DISCARD).unwrap();
-        match trap.arm(Some(&mut blocking_events_buf)) {
-            ArmResult::Armed => (),
-            ArmResult::Blocked(events) => panic!("unexpected blocking events {:?}", events),
-            ArmResult::Failed(e) => panic!("unexpected Mojo error {:?}", e),
-        }
-
-        // Close `prod` making `cons` permanently unreadable.
-        drop(prod);
-
-        // Now we should have two events: one to indicate that cons will never
-        // be readable again, and one to indicate that `prod` has been closed
-        // and removed from the trap.
-        {
-            let list = wait_for_trap_events(TRAP_EVENT_LIST.lock().unwrap(), 1);
-            assert_eq!(list.len(), 2);
-            let (event1, event2) = (list[0], list[1]);
-            // Sort the events since the ordering isn't deterministic.
-            let (cons_event, prod_event) = if event1.trigger_context() == 1 {
-                (event1, event2)
-            } else {
-                (event2, event1)
-            };
-
-            // 1. `cons` can no longer be readable.
-            assert_eq!(cons_event.trigger_context(), 1);
-            assert_eq!(cons_event.result(), MojoResult::FailedPrecondition);
-            assert!(!cons_event.signals_state().satisfiable().is_readable());
-
-            // 2. `prod` was closed, yielding a `Cancelled` event.
-            assert_eq!(prod_event.trigger_context(), 2);
-            assert_eq!(prod_event.result(), MojoResult::Cancelled);
-        };
-
-        drop(trap);
-
-        // We should have 3 events: the two we saw above, plus one Cancelled
-        // event for `prod` corresponding to removing `prod` from `trap` (which
-        // happens automatically on `Trap` closure).
-        clear_trap_events(3);
-    }
-
-    fn trap_handle_closed_before_arm() {
-        let _test_lock = TRAP_TEST_LOCK.lock().unwrap();
-
-        let trap = UnsafeTrap::new(test_trap_event_handler).unwrap();
-
-        let (cons, _prod) = data_pipe::create_default().unwrap();
-        assert_eq!(MojoResult::Okay,
-            trap.add_trigger(cons.get_native_handle(),
-                             HandleSignals::READABLE,
-                             TriggerCondition::SignalsSatisfied, 1));
-
-        drop(cons);
-
-        // A cancelled event will be reported even without arming.
-        {
-            let events = wait_for_trap_events(TRAP_EVENT_LIST.lock().unwrap(), 1);
-            assert_eq!(events.len(), 1, "unexpected events {:?}", *events);
-            let event = events[0];
-            assert_eq!(event.trigger_context(), 1);
-            assert_eq!(event.result(), MojoResult::Cancelled);
-        }
-
-        drop(trap);
-        clear_trap_events(1);
-    }
-
-    fn safe_trap() {
-        struct SharedContext {
-            events: Mutex<Vec<TrapEvent>>,
-            cond: Condvar,
-        }
-
-        let handler = |event: &TrapEvent, context: &Arc<SharedContext>| {
-            if let Ok(mut events) = context.events.lock() {
-                events.push(*event);
-                context.cond.notify_all();
-            }
-        };
-
-        let context = Arc::new(SharedContext {
-            events: Mutex::new(Vec::new()),
-            cond: Condvar::new(),
-        });
-        let trap = Trap::new(handler).unwrap();
-
-        let (cons, prod) = data_pipe::create_default().unwrap();
-        let _cons_token = trap.add_trigger(
+    let (cons, prod) = data_pipe::create_default().unwrap();
+    assert_eq!(
+        MojoResult::Okay,
+        trap.add_trigger(
             cons.get_native_handle(),
             HandleSignals::READABLE,
             TriggerCondition::SignalsSatisfied,
-            context.clone());
-        let _prod_token = trap.add_trigger(
+            1
+        )
+    );
+    assert_eq!(
+        MojoResult::Okay,
+        trap.add_trigger(
             prod.get_native_handle(),
             HandleSignals::PEER_CLOSED,
             TriggerCondition::SignalsSatisfied,
-            context.clone());
+            2
+        )
+    );
 
-        assert_eq!(trap.arm(), MojoResult::Okay);
+    let mut blocking_events_buf = [std::mem::MaybeUninit::uninit(); 16];
+    // The trap should arm with no blocking events since nothing should be
+    // triggered yet.
+    match trap.arm(Some(&mut blocking_events_buf)) {
+        ArmResult::Armed => (),
+        ArmResult::Blocked(events) => panic!("unexpected blocking events {:?}", events),
+        ArmResult::Failed(e) => panic!("unexpected mojo error {:?}", e),
+    }
 
-        // Make `cons` readable.
-        assert_eq!(prod.write(&[128u8], data_pipe::WriteFlags::empty()), Ok(1));
-        {
-            let mut events =
-                context.cond.wait_while(context.events.lock().unwrap(), |e| e.is_empty()).unwrap();
-            assert_eq!(events.len(), 1, "unexpected events {:?}", events);
-            let event = events[0];
-            assert_eq!(event.handle(), cons.get_native_handle());
+    // Check that there are no events in the list (though of course this
+    // check is uncertain if a race condition bug exists).
+    assert_eq!(TRAP_EVENT_LIST.lock().unwrap().len(), 0);
+
+    // Write to `prod` making `cons` readable.
+    assert_eq!(prod.write(&[128u8], data_pipe::WriteFlags::empty()).unwrap(), 1);
+    {
+        let list = wait_for_trap_events(TRAP_EVENT_LIST.lock().unwrap(), 1);
+        assert_eq!(list.len(), 1);
+        let event = list[0];
+        assert_eq!(event.trigger_context(), 1);
+        assert_eq!(event.result(), MojoResult::Okay);
+        assert!(event.signals_state().satisfiable().is_readable(), "{:?}", event.signals_state());
+        assert!(event.signals_state().satisfied().is_readable(), "{:?}", event.signals_state());
+    }
+
+    // Once the above event has fired, `trap` is disarmed.
+
+    // Re-arming should block and return the event above.
+    match trap.arm(Some(&mut blocking_events_buf)) {
+        ArmResult::Blocked(events) => {
+            let event: &UnsafeTrapEvent = events.get(0).unwrap();
+            assert_eq!(event.trigger_context(), 1);
             assert_eq!(event.result(), MojoResult::Okay);
-            assert!(event.signals_state().satisfied().is_readable(), "{:?}", event.signals_state());
-            events.clear();
         }
+        ArmResult::Armed => panic!("expected event did not arrive"),
+        ArmResult::Failed(e) => panic!("unexpected Mojo error {:?}", e),
+    }
 
-        // Close `cons` to get two events: peer closure on `prod`, and Cancelled on `cons`.
-        let cons_native = cons.get_native_handle();
-        drop(cons);
-        {
-            // We get the Cancelled event while unarmed.
-            let mut events =
-                context.cond.wait_while(context.events.lock().unwrap(), |e| e.is_empty()).unwrap();
-            assert_eq!(events.len(), 1, "unexpected events {:?}", events);
-            let event = events[0];
-            assert_eq!(event.handle(), cons_native);
-            assert_eq!(event.result(), MojoResult::Cancelled);
-            events.clear();
-        }
+    clear_trap_events(1);
 
-        // When we try to arm, we'll get the `prod` event.
-        assert_eq!(trap.arm(), MojoResult::FailedPrecondition);
-        {
-            let mut events =
-                context.cond.wait_while(context.events.lock().unwrap(), |e| e.is_empty()).unwrap();
-            assert_eq!(events.len(), 1, "unexpected events {:?}", events);
-            let event = events[0];
-            assert_eq!(event.handle(), prod.get_native_handle());
-            assert_eq!(event.result(), MojoResult::Okay);
-            assert!(event.signals_state().satisfied().is_peer_closed(),
-                    "{:?}", event.signals_state());
-            events.clear();
+    // Read the data so we don't receive the same event again.
+    cons.read(data_pipe::ReadFlags::DISCARD).unwrap();
+    match trap.arm(Some(&mut blocking_events_buf)) {
+        ArmResult::Armed => (),
+        ArmResult::Blocked(events) => panic!("unexpected blocking events {:?}", events),
+        ArmResult::Failed(e) => panic!("unexpected Mojo error {:?}", e),
+    }
+
+    // Close `prod` making `cons` permanently unreadable.
+    drop(prod);
+
+    // Now we should have two events: one to indicate that cons will never
+    // be readable again, and one to indicate that `prod` has been closed
+    // and removed from the trap.
+    {
+        let list = wait_for_trap_events(TRAP_EVENT_LIST.lock().unwrap(), 1);
+        assert_eq!(list.len(), 2);
+        let (event1, event2) = (list[0], list[1]);
+        // Sort the events since the ordering isn't deterministic.
+        let (cons_event, prod_event) =
+            if event1.trigger_context() == 1 { (event1, event2) } else { (event2, event1) };
+
+        // 1. `cons` can no longer be readable.
+        assert_eq!(cons_event.trigger_context(), 1);
+        assert_eq!(cons_event.result(), MojoResult::FailedPrecondition);
+        assert!(!cons_event.signals_state().satisfiable().is_readable());
+
+        // 2. `prod` was closed, yielding a `Cancelled` event.
+        assert_eq!(prod_event.trigger_context(), 2);
+        assert_eq!(prod_event.result(), MojoResult::Cancelled);
+    };
+
+    drop(trap);
+
+    // We should have 3 events: the two we saw above, plus one Cancelled
+    // event for `prod` corresponding to removing `prod` from `trap` (which
+    // happens automatically on `Trap` closure).
+    clear_trap_events(3);
+});
+
+mojo_test!(trap_handle_closed_before_arm, {
+    let _test_lock = TRAP_TEST_LOCK.lock().unwrap();
+
+    let trap = UnsafeTrap::new(test_trap_event_handler).unwrap();
+
+    let (cons, _prod) = data_pipe::create_default().unwrap();
+    assert_eq!(
+        MojoResult::Okay,
+        trap.add_trigger(
+            cons.get_native_handle(),
+            HandleSignals::READABLE,
+            TriggerCondition::SignalsSatisfied,
+            1
+        )
+    );
+
+    drop(cons);
+
+    // A cancelled event will be reported even without arming.
+    {
+        let events = wait_for_trap_events(TRAP_EVENT_LIST.lock().unwrap(), 1);
+        assert_eq!(events.len(), 1, "unexpected events {:?}", *events);
+        let event = events[0];
+        assert_eq!(event.trigger_context(), 1);
+        assert_eq!(event.result(), MojoResult::Cancelled);
+    }
+
+    drop(trap);
+    clear_trap_events(1);
+});
+
+mojo_test!(safe_trap, {
+    struct SharedContext {
+        events: Mutex<Vec<TrapEvent>>,
+        cond: Condvar,
+    }
+
+    let handler = |event: &TrapEvent, context: &Arc<SharedContext>| {
+        if let Ok(mut events) = context.events.lock() {
+            events.push(*event);
+            context.cond.notify_all();
         }
-     }
-}
+    };
+
+    let context = Arc::new(SharedContext { events: Mutex::new(Vec::new()), cond: Condvar::new() });
+    let trap = Trap::new(handler).unwrap();
+
+    let (cons, prod) = data_pipe::create_default().unwrap();
+    let _cons_token = trap.add_trigger(
+        cons.get_native_handle(),
+        HandleSignals::READABLE,
+        TriggerCondition::SignalsSatisfied,
+        context.clone(),
+    );
+    let _prod_token = trap.add_trigger(
+        prod.get_native_handle(),
+        HandleSignals::PEER_CLOSED,
+        TriggerCondition::SignalsSatisfied,
+        context.clone(),
+    );
+
+    assert_eq!(trap.arm(), MojoResult::Okay);
+
+    // Make `cons` readable.
+    assert_eq!(prod.write(&[128u8], data_pipe::WriteFlags::empty()), Ok(1));
+    {
+        let mut events =
+            context.cond.wait_while(context.events.lock().unwrap(), |e| e.is_empty()).unwrap();
+        assert_eq!(events.len(), 1, "unexpected events {:?}", events);
+        let event = events[0];
+        assert_eq!(event.handle(), cons.get_native_handle());
+        assert_eq!(event.result(), MojoResult::Okay);
+        assert!(event.signals_state().satisfied().is_readable(), "{:?}", event.signals_state());
+        events.clear();
+    }
+
+    // Close `cons` to get two events: peer closure on `prod`, and Cancelled on
+    // `cons`.
+    let cons_native = cons.get_native_handle();
+    drop(cons);
+    {
+        // We get the Cancelled event while unarmed.
+        let mut events =
+            context.cond.wait_while(context.events.lock().unwrap(), |e| e.is_empty()).unwrap();
+        assert_eq!(events.len(), 1, "unexpected events {:?}", events);
+        let event = events[0];
+        assert_eq!(event.handle(), cons_native);
+        assert_eq!(event.result(), MojoResult::Cancelled);
+        events.clear();
+    }
+
+    // When we try to arm, we'll get the `prod` event.
+    assert_eq!(trap.arm(), MojoResult::FailedPrecondition);
+    {
+        let mut events =
+            context.cond.wait_while(context.events.lock().unwrap(), |e| e.is_empty()).unwrap();
+        assert_eq!(events.len(), 1, "unexpected events {:?}", events);
+        let event = events[0];
+        assert_eq!(event.handle(), prod.get_native_handle());
+        assert_eq!(event.result(), MojoResult::Okay);
+        assert!(event.signals_state().satisfied().is_peer_closed(), "{:?}", event.signals_state());
+        events.clear();
+    }
+});
 
 fn clear_trap_events(expected_len: usize) {
     let mut list = TRAP_EVENT_LIST.lock().unwrap();
diff --git a/mojo/public/rust/tests/util/mod.rs b/mojo/public/rust/tests/util/mod.rs
index 075e69754..161add8 100644
--- a/mojo/public/rust/tests/util/mod.rs
+++ b/mojo/public/rust/tests/util/mod.rs
@@ -13,20 +13,21 @@
 use std::slice;
 use std::vec::Vec;
 
-/// This macro sets up tests by adding in Mojo embedder
-/// initialization.
-macro_rules! tests {
-    { $( $( #[ $attr:meta ] )* fn $i:ident() $b:block)* } => {
+/// This macro sets up tests by adding in Mojo embedder initialization.
+///
+/// Note: this macro is quite delicate because of rustmt's inconsistent handling
+/// of macro invocations. Slight changes to macro syntax can make rustfmt ignore
+/// the inside of an invocation, which is not what we want.
+macro_rules! mojo_test {
+    {$i: ident, $(#[$attr:meta])* $b:block} => {
+        #[test]
         $(
-            #[test]
-            $(
-            #[ $attr ]
-            )*
-            fn $i() {
-                $crate::util::init();
-                $b
-            }
+        #[ $attr ]
         )*
+        fn $i() {
+            $crate::util::init();
+            $b
+        }
     }
 }
 
diff --git a/mojo/public/rust/tests/validation.rs b/mojo/public/rust/tests/validation.rs
index 1e20f46..154fb43 100644
--- a/mojo/public/rust/tests/validation.rs
+++ b/mojo/public/rust/tests/validation.rs
@@ -24,31 +24,29 @@
 ///      error.
 macro_rules! validation_tests {
     ($($name:ident => $req_type:ident;)*) => {
-        tests! {
-            $(
-            fn $name() {
-                let data = include_str!(concat!("../../interfaces/bindings/tests/data/validation/",
+        $(
+        mojo_test!($name, {
+            let data = include_str!(concat!("../../interfaces/bindings/tests/data/validation/",
+                                            stringify!($name),
+                                            ".data"));
+            let expected = include_str!(concat!("../../interfaces/bindings/tests/data/validation/",
                                                 stringify!($name),
-                                                ".data"));
-                let expected = include_str!(concat!("../../interfaces/bindings/tests/data/validation/",
-                                                    stringify!($name),
-                                                    ".expected")).trim();
-                match util::parse_validation_test(data) {
-                    Ok((data, num_handles)) => {
-                        let mut mock_handles = Vec::with_capacity(num_handles);
-                        for _ in 0..num_handles {
-                            mock_handles.push(unsafe { system::acquire(0) });
-                        }
-                        match $req_type::decode_message(data, mock_handles) {
-                            Ok(_) => panic!("Should not be valid!"),
-                            Err(err) => assert_eq!(err.as_str(), expected),
-                        }
-                    },
-                    Err(msg) => panic!("Error: {}", msg),
-                }
+                                                ".expected")).trim();
+            match util::parse_validation_test(data) {
+                Ok((data, num_handles)) => {
+                    let mut mock_handles = Vec::with_capacity(num_handles);
+                    for _ in 0..num_handles {
+                        mock_handles.push(unsafe { system::acquire(0) });
+                    }
+                    match $req_type::decode_message(data, mock_handles) {
+                        Ok(_) => panic!("Should not be valid!"),
+                        Err(err) => assert_eq!(err.as_str(), expected),
+                    }
+                },
+                Err(msg) => panic!("Error: {}", msg),
             }
-            )*
-        }
+        });
+        )*
     }
 }
 
diff --git a/net/base/features.cc b/net/base/features.cc
index 59a8203..7c048f8 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -77,6 +77,10 @@
              "EncryptedClientHello",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kEncryptedClientHelloQuic,
+             "EncryptedClientHelloQuic",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kNetworkQualityEstimator,
              "NetworkQualityEstimator",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/net/base/features.h b/net/base/features.h
index e72d8dd..7a5062b2d 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -98,6 +98,13 @@
 // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-13
 NET_EXPORT BASE_DECLARE_FEATURE(kEncryptedClientHello);
 
+// Enables the TLS Encrypted ClientHello feature for QUIC. Only takes effect if
+// kEncryptedClientHello is also enabled.
+//
+// TODO(crbug.com/1287248): Remove this flag when ECH for QUIC is fully
+// implemented. This flag is just a temporary mechanism for now.
+NET_EXPORT BASE_DECLARE_FEATURE(kEncryptedClientHelloQuic);
+
 // Enables optimizing the network quality estimation algorithms in network
 // quality estimator (NQE).
 NET_EXPORT BASE_DECLARE_FEATURE(kNetworkQualityEstimator);
diff --git a/net/base/isolation_info_unittest.cc b/net/base/isolation_info_unittest.cc
index 13b0cf0e..91145c85 100644
--- a/net/base/isolation_info_unittest.cc
+++ b/net/base/isolation_info_unittest.cc
@@ -179,10 +179,7 @@
 
 // A 2.5-keyed NAK created with two identical opaque origins should be
 // same-site.
-// TODO(crbug.com/1419563): Disabled as it was causing consistent failures
-// across multiple builders
-TEST_P(IsolationInfoTest,
-       DISABLED_CreateNetworkAnonymizationKeyForIsolationInfoOpaque) {
+TEST_P(IsolationInfoTest, CreateNetworkAnonymizationKeyForIsolationInfoOpaque) {
   url::Origin opaque;
   IsolationInfo isolation_info = IsolationInfo::Create(
       IsolationInfo::RequestType::kMainFrame, opaque, opaque,
@@ -194,7 +191,7 @@
   if (IsDoubleKeyAndCrossSiteBitNetworkAnonymizationKeyEnabled()) {
     EXPECT_FALSE(nak.GetIsCrossSite().value());
   } else {
-    EXPECT_DEATH_IF_SUPPORTED(nak.GetIsCrossSite(), "");
+    EXPECT_DCHECK_DEATH(nak.GetIsCrossSite());
   }
 
   url::Origin opaque2;
diff --git a/net/cert/internal/system_trust_store.cc b/net/cert/internal/system_trust_store.cc
index ee580831..e3e1780 100644
--- a/net/cert/internal/system_trust_store.cc
+++ b/net/cert/internal/system_trust_store.cc
@@ -185,7 +185,7 @@
 
 std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
   return std::make_unique<SystemTrustStoreNSS>(std::make_unique<TrustStoreNSS>(
-      trustSSL, TrustStoreNSS::kUseSystemTrust,
+      TrustStoreNSS::kUseSystemTrust,
       TrustStoreNSS::UseTrustFromAllUserSlots()));
 }
 
@@ -194,7 +194,7 @@
     std::unique_ptr<TrustStoreChrome> chrome_root) {
   return std::make_unique<SystemTrustStoreChrome>(
       std::move(chrome_root), std::make_unique<TrustStoreNSS>(
-                                  trustSSL, TrustStoreNSS::kIgnoreSystemTrust,
+                                  TrustStoreNSS::kIgnoreSystemTrust,
                                   TrustStoreNSS::UseTrustFromAllUserSlots()));
 }
 
@@ -203,9 +203,9 @@
     std::unique_ptr<TrustStoreChrome> chrome_root,
     crypto::ScopedPK11Slot user_slot_restriction) {
   return std::make_unique<SystemTrustStoreChrome>(
-      std::move(chrome_root), std::make_unique<TrustStoreNSS>(
-                                  trustSSL, TrustStoreNSS::kIgnoreSystemTrust,
-                                  std::move(user_slot_restriction)));
+      std::move(chrome_root),
+      std::make_unique<TrustStoreNSS>(TrustStoreNSS::kIgnoreSystemTrust,
+                                      std::move(user_slot_restriction)));
 }
 
 #endif  // CHROME_ROOT_STORE_SUPPORTED
@@ -213,9 +213,8 @@
 std::unique_ptr<SystemTrustStore>
 CreateSslSystemTrustStoreNSSWithUserSlotRestriction(
     crypto::ScopedPK11Slot user_slot_restriction) {
-  return std::make_unique<SystemTrustStoreNSS>(
-      std::make_unique<TrustStoreNSS>(trustSSL, TrustStoreNSS::kUseSystemTrust,
-                                      std::move(user_slot_restriction)));
+  return std::make_unique<SystemTrustStoreNSS>(std::make_unique<TrustStoreNSS>(
+      TrustStoreNSS::kUseSystemTrust, std::move(user_slot_restriction)));
 }
 
 #elif BUILDFLAG(IS_MAC)
diff --git a/net/cert/internal/trust_store_nss.cc b/net/cert/internal/trust_store_nss.cc
index 6822a21b..bc3470a 100644
--- a/net/cert/internal/trust_store_nss.cc
+++ b/net/cert/internal/trust_store_nss.cc
@@ -70,28 +70,11 @@
   return r;
 }
 
-CK_ATTRIBUTE_TYPE SecTrustTypeToAttributeType(SECTrustType trust_type) {
-  switch (trust_type) {
-    case trustSSL:
-      return CKA_TRUST_SERVER_AUTH;
-    case trustEmail:
-      return CKA_TRUST_EMAIL_PROTECTION;
-    case trustObjectSigning:
-      return CKA_TRUST_CODE_SIGNING;
-    case trustTypeNone:
-      NOTREACHED();
-      return 0;
-  }
-}
-
 }  // namespace
 
-TrustStoreNSS::TrustStoreNSS(SECTrustType trust_type,
-                             SystemTrustSetting system_trust_setting,
+TrustStoreNSS::TrustStoreNSS(SystemTrustSetting system_trust_setting,
                              UserSlotTrustSetting user_slot_trust_setting)
-    : trust_type_(trust_type),
-      trust_attr_type_(SecTrustTypeToAttributeType(trust_type)),
-      ignore_system_trust_settings_(system_trust_setting == kIgnoreSystemTrust),
+    : ignore_system_trust_settings_(system_trust_setting == kIgnoreSystemTrust),
       user_slot_trust_setting_(std::move(user_slot_trust_setting)) {}
 
 TrustStoreNSS::~TrustStoreNSS() = default;
@@ -316,7 +299,7 @@
       crypto::ScopedSECItem trust_attr(SECITEM_AllocItem(/*arena=*/nullptr,
                                                          /*item=*/nullptr,
                                                          /*len=*/0));
-      rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, trust_attr_type_,
+      rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TRUST_SERVER_AUTH,
                                  trust_attr.get());
       if (rv != SECSuccess) {
         DVLOG(1) << "trust object for " << base::HexEncode(trust_obj_sha1)
@@ -423,7 +406,7 @@
 
 CertificateTrust TrustStoreNSS::GetTrustForNSSTrust(
     const CERTCertTrust& trust) const {
-  unsigned int trust_flags = SEC_GET_TRUST_FLAGS(&trust, trust_type_);
+  unsigned int trust_flags = SEC_GET_TRUST_FLAGS(&trust, trustSSL);
 
   // Determine if the certificate is distrusted.
   if ((trust_flags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED_CA |
diff --git a/net/cert/internal/trust_store_nss.h b/net/cert/internal/trust_store_nss.h
index 67d2fc3..d37b482 100644
--- a/net/cert/internal/trust_store_nss.h
+++ b/net/cert/internal/trust_store_nss.h
@@ -29,7 +29,7 @@
       absl::variant<UseTrustFromAllUserSlots, crypto::ScopedPK11Slot>;
 
   // Creates a TrustStoreNSS which will find anchors that are trusted for
-  // |trust_type|.
+  // SSL server auth.
   //
   // |system_trust_setting| configures the use of trust from the builtin roots.
   // If |system_trust_setting| is kIgnoreSystemTrust, trust settings from the
@@ -39,8 +39,7 @@
   //  * UseTrustFromAllUserSlots: all user slots will be allowed.
   //  * nullptr: no user slots will be allowed.
   //  * non-null PK11Slot: the specified slot will be allowed.
-  TrustStoreNSS(SECTrustType trust_type,
-                SystemTrustSetting system_trust_setting,
+  TrustStoreNSS(SystemTrustSetting system_trust_setting,
                 UserSlotTrustSetting user_slot_trust_setting);
 
   TrustStoreNSS(const TrustStoreNSS&) = delete;
@@ -67,9 +66,6 @@
       const ParsedCertificate* cert,
       base::SupportsUserData* debug_data) const;
 
-  const SECTrustType trust_type_;
-  const CK_ATTRIBUTE_TYPE trust_attr_type_;
-
   // |ignore_system_certs_trust_settings_| specifies if the system trust
   // settings should be considered when determining a cert's trustworthiness.
   const bool ignore_system_trust_settings_ = false;
diff --git a/net/cert/internal/trust_store_nss_unittest.cc b/net/cert/internal/trust_store_nss_unittest.cc
index bb1218c..c2a8133 100644
--- a/net/cert/internal/trust_store_nss_unittest.cc
+++ b/net/cert/internal/trust_store_nss_unittest.cc
@@ -487,15 +487,14 @@
     switch (slot_filter_type()) {
       case SlotFilterType::kDontFilter:
         return std::make_unique<TrustStoreNSS>(
-            trustSSL, system_trust_setting(),
-            TrustStoreNSS::UseTrustFromAllUserSlots());
+            system_trust_setting(), TrustStoreNSS::UseTrustFromAllUserSlots());
       case SlotFilterType::kDoNotAllowUserSlots:
         return std::make_unique<TrustStoreNSS>(
-            trustSSL, system_trust_setting(),
+            system_trust_setting(),
             /*user_slot_trust_setting=*/nullptr);
       case SlotFilterType::kAllowSpecifiedUserSlot:
         return std::make_unique<TrustStoreNSS>(
-            trustSSL, system_trust_setting(),
+            system_trust_setting(),
             crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())));
     }
   }
@@ -570,7 +569,7 @@
 
   std::unique_ptr<TrustStoreNSS> CreateTrustStoreNSS() override {
     return std::make_unique<TrustStoreNSS>(
-        trustSSL, TrustStoreNSS::kIgnoreSystemTrust,
+        TrustStoreNSS::kIgnoreSystemTrust,
         TrustStoreNSS::UseTrustFromAllUserSlots());
   }
 };
@@ -777,7 +776,7 @@
 
   std::unique_ptr<TrustStoreNSS> CreateTrustStoreNSS() override {
     return std::make_unique<TrustStoreNSS>(
-        trustSSL, TrustStoreNSS::kUseSystemTrust,
+        TrustStoreNSS::kUseSystemTrust,
         TrustStoreNSS::UseTrustFromAllUserSlots());
   }
 };
@@ -915,7 +914,7 @@
   }
 
   std::unique_ptr<TrustStoreNSS> CreateTrustStoreNSS() override {
-    return std::make_unique<TrustStoreNSS>(trustSSL, system_trust_setting(),
+    return std::make_unique<TrustStoreNSS>(system_trust_setting(),
                                            /*user_slot_trust_setting=*/nullptr);
   }
 };
@@ -982,7 +981,7 @@
 
   std::unique_ptr<TrustStoreNSS> CreateTrustStoreNSS() override {
     return std::make_unique<TrustStoreNSS>(
-        trustSSL, system_trust_setting(),
+        system_trust_setting(),
         crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())));
   }
 };
@@ -1084,8 +1083,7 @@
 class TrustStoreNSSTestDelegate {
  public:
   TrustStoreNSSTestDelegate()
-      : trust_store_nss_(trustSSL,
-                         TrustStoreNSS::kUseSystemTrust,
+      : trust_store_nss_(TrustStoreNSS::kUseSystemTrust,
                          TrustStoreNSS::UseTrustFromAllUserSlots()) {}
 
   void AddCert(std::shared_ptr<const ParsedCertificate> cert) {
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 0804b96d..95a93d7 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -21,6 +21,7 @@
 #include "net/base/load_timing_info.h"
 #include "net/base/load_timing_info_test_util.h"
 #include "net/base/net_errors.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/bidirectional_stream_request_info.h"
 #include "net/http/transport_security_state.h"
@@ -44,6 +45,7 @@
 #include "net/quic/test_quic_crypto_client_config_handle.h"
 #include "net/quic/test_task_runner.h"
 #include "net/socket/socket_test_util.h"
+#include "net/ssl/ssl_config_service_defaults.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_with_task_environment.h"
 #include "net/third_party/quiche/src/quiche/common/quiche_text_utils.h"
@@ -512,7 +514,7 @@
     session_ = std::make_unique<QuicChromiumClientSession>(
         connection_, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_, /*ssl_config_service=*/nullptr,
+        &transport_security_state_, &ssl_config_service_,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
         QuicSessionKey(kDefaultServerHostName, kDefaultServerPort,
                        PRIVACY_MODE_DISABLED, SocketTag(),
@@ -537,7 +539,8 @@
         std::make_unique<quic::QuicClientPushPromiseIndex>(), nullptr,
         base::DefaultTickClock::GetInstance(),
         base::SingleThreadTaskRunner::GetCurrentDefault().get(),
-        /*socket_performance_watcher=*/nullptr, NetLog::Get());
+        /*socket_performance_watcher=*/nullptr, HostResolverEndpointResult(),
+        NetLog::Get());
     session_->Initialize();
 
     // Blackhole QPACK decoder stream instead of constructing mock writes.
@@ -780,6 +783,7 @@
   std::unique_ptr<QuicChromiumConnectionHelper> helper_;
   std::unique_ptr<QuicChromiumAlarmFactory> alarm_factory_;
   TransportSecurityState transport_security_state_;
+  SSLConfigServiceDefaults ssl_config_service_;
   std::unique_ptr<QuicChromiumClientSession> session_;
   quic::QuicCryptoClientConfig crypto_config_;
   HttpRequestHeaders headers_;
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index b4844da..0a3a15f5 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -35,6 +35,7 @@
 #include "net/base/privacy_mode.h"
 #include "net/base/url_util.h"
 #include "net/cert/signed_certificate_timestamp_and_status.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/http/transport_security_state.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source_type.h"
@@ -998,6 +999,7 @@
     const base::TickClock* tick_clock,
     base::SequencedTaskRunner* task_runner,
     std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,
+    const HostResolverEndpointResult& endpoint_result,
     NetLog* net_log)
     : quic::QuicSpdyClientSessionBase(connection,
                                       /*visitor=*/nullptr,
@@ -1040,7 +1042,8 @@
       http3_logger_(std::make_unique<QuicHttp3Logger>(net_log_)),
       push_delegate_(push_delegate),
       push_promise_index_(std::move(push_promise_index)),
-      path_validation_writer_delegate_(this, task_runner_) {
+      path_validation_writer_delegate_(this, task_runner_),
+      ech_config_list_(endpoint_result.metadata.ech_config_list) {
   default_network_ = default_network;
   auto* socket_raw = socket.get();
   sockets_.push_back(std::move(socket));
@@ -1440,6 +1443,7 @@
 
   ssl_info->key_exchange_group = crypto_params.key_exchange_group;
   ssl_info->peer_signature_algorithm = crypto_params.peer_signature_algorithm;
+  ssl_info->encrypted_client_hello = crypto_params.encrypted_client_hello;
   return true;
 }
 
@@ -1653,6 +1657,18 @@
   }
 }
 
+quic::QuicSSLConfig QuicChromiumClientSession::GetSSLConfig() const {
+  quic::QuicSSLConfig config = quic::QuicSpdyClientSessionBase::GetSSLConfig();
+  if (ssl_config_service_->GetSSLContextConfig()
+          .EncryptedClientHelloEnabled() &&
+      base::FeatureList::IsEnabled(features::kEncryptedClientHelloQuic)) {
+    config.ech_grease_enabled = true;
+    config.ech_config_list.assign(ech_config_list_.begin(),
+                                  ech_config_list_.end());
+  }
+  return config;
+}
+
 void QuicChromiumClientSession::OnConfigNegotiated() {
   quic::QuicSpdyClientSessionBase::OnConfigNegotiated();
   if (!stream_factory_ || !stream_factory_->allow_server_migration()) {
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index fac69b4..604b5e3 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -65,6 +65,7 @@
 
 class CertVerifyResult;
 class DatagramClientSocket;
+struct HostResolverEndpointResult;
 class NetLog;
 class QuicCryptoClientStreamFactory;
 class QuicServerInfo;
@@ -623,6 +624,7 @@
       const base::TickClock* tick_clock,
       base::SequencedTaskRunner* task_runner,
       std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,
+      const HostResolverEndpointResult& endpoint_result,
       NetLog* net_log);
 
   QuicChromiumClientSession(const QuicChromiumClientSession&) = delete;
@@ -727,6 +729,7 @@
       const quic::CryptoHandshakeMessage& message) override;
   void OnGoAway(const quic::QuicGoAwayFrame& frame) override;
   void OnCanCreateNewOutgoingStream(bool unidirectional) override;
+  quic::QuicSSLConfig GetSSLConfig() const override;
 
   // QuicSpdyClientSessionBase methods:
   void OnConfigNegotiated() override;
@@ -1173,6 +1176,8 @@
   base::flat_map<url::SchemeHostPort, std::string>
       accept_ch_entries_received_via_alps_;
 
+  std::vector<uint8_t> ech_config_list_;
+
   base::WeakPtrFactory<QuicChromiumClientSession> weak_factory_{this};
 };
 
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 77c17c217..62b1d98 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -20,6 +20,7 @@
 #include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/cert_verify_result.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/transport_security_state.h"
 #include "net/http/transport_security_state_test_util.h"
@@ -46,6 +47,7 @@
 #include "net/socket/datagram_client_socket.h"
 #include "net/socket/socket_test_util.h"
 #include "net/spdy/spdy_test_util_common.h"
+#include "net/ssl/ssl_config_service_defaults.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
@@ -168,7 +170,7 @@
     session_ = std::make_unique<TestingQuicChromiumClientSession>(
         connection, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        transport_security_state_.get(), /*ssl_config_service=*/nullptr,
+        transport_security_state_.get(), &ssl_config_service_,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)), session_key_,
         /*require_confirmation=*/false, migrate_session_early_v2_,
         /*migrate_session_on_network_change_v2=*/false, default_network_,
@@ -187,7 +189,8 @@
         std::make_unique<quic::QuicClientPushPromiseIndex>(),
         &test_push_delegate_, base::DefaultTickClock::GetInstance(),
         base::SingleThreadTaskRunner::GetCurrentDefault().get(),
-        /*socket_performance_watcher=*/nullptr, NetLog::Get());
+        /*socket_performance_watcher=*/nullptr, HostResolverEndpointResult(),
+        NetLog::Get());
     if (connectivity_monitor_) {
       connectivity_monitor_->SetInitialDefaultNetwork(default_network_);
       session_->AddConnectivityObserver(connectivity_monitor_.get());
@@ -263,6 +266,7 @@
   quic::test::MockAlarmFactory alarm_factory_;
   std::unique_ptr<TransportSecurityState> transport_security_state_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
+  SSLConfigServiceDefaults ssl_config_service_;
   QuicSessionKey session_key_;
   url::SchemeHostPort destination_;
   std::unique_ptr<TestingQuicChromiumClientSession> session_;
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 7cc35e4..b6c66a2 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -29,6 +29,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/upload_bytes_element_reader.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/transport_security_state.h"
@@ -55,6 +56,7 @@
 #include "net/socket/socket_performance_watcher.h"
 #include "net/socket/socket_test_util.h"
 #include "net/spdy/spdy_http_utils.h"
+#include "net/ssl/ssl_config_service_defaults.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
@@ -397,7 +399,7 @@
     session_ = std::make_unique<QuicChromiumClientSession>(
         connection_, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_, /*ssl_config_service=*/nullptr,
+        &transport_security_state_, &ssl_config_service_,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
         QuicSessionKey(kDefaultServerHostName, kDefaultServerPort,
                        PRIVACY_MODE_DISABLED, SocketTag(),
@@ -422,7 +424,8 @@
         std::make_unique<quic::QuicClientPushPromiseIndex>(), nullptr,
         base::DefaultTickClock::GetInstance(),
         base::SingleThreadTaskRunner::GetCurrentDefault().get(),
-        /*socket_performance_watcher=*/nullptr, NetLog::Get());
+        /*socket_performance_watcher=*/nullptr, HostResolverEndpointResult(),
+        NetLog::Get());
     session_->Initialize();
 
     // Blackhole QPACK decoder stream instead of constructing mock writes.
@@ -650,6 +653,7 @@
   std::unique_ptr<UploadDataStream> upload_data_stream_;
   std::unique_ptr<QuicHttpStream> stream_;
   TransportSecurityState transport_security_state_;
+  SSLConfigServiceDefaults ssl_config_service_;
 
   // Must outlive `send_algorithm_` and `connection_`.
   std::unique_ptr<QuicChromiumClientSession> session_;
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 4a0759d..d71af46b 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -21,6 +21,7 @@
 #include "net/base/proxy_string_util.h"
 #include "net/base/test_proxy_delegate.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_auth_cache.h"
 #include "net/http/http_auth_handler_factory.h"
@@ -46,6 +47,7 @@
 #include "net/quic/test_quic_crypto_client_config_handle.h"
 #include "net/quic/test_task_runner.h"
 #include "net/socket/socket_test_util.h"
+#include "net/ssl/ssl_config_service_defaults.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
@@ -233,7 +235,7 @@
     session_ = std::make_unique<QuicChromiumClientSession>(
         connection, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_, /*ssl_config_service=*/nullptr,
+        &transport_security_state_, &ssl_config_service_,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
         QuicSessionKey("mail.example.org", 80, PRIVACY_MODE_DISABLED,
                        SocketTag(), NetworkAnonymizationKey(),
@@ -258,7 +260,8 @@
         std::make_unique<quic::QuicClientPushPromiseIndex>(), nullptr,
         base::DefaultTickClock::GetInstance(),
         base::SingleThreadTaskRunner::GetCurrentDefault().get(),
-        /*socket_performance_watcher=*/nullptr, NetLog::Get());
+        /*socket_performance_watcher=*/nullptr, HostResolverEndpointResult(),
+        NetLog::Get());
 
     writer->set_delegate(session_.get());
 
@@ -594,6 +597,7 @@
   std::unique_ptr<QuicChromiumAlarmFactory> alarm_factory_;
   testing::StrictMock<quic::test::MockQuicConnectionVisitor> visitor_;
   TransportSecurityState transport_security_state_;
+  SSLConfigServiceDefaults ssl_config_service_;
   quic::QuicCryptoClientConfig crypto_config_;
 
   const quic::QuicConnectionId connection_id_;
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index b6715ef6..a84782cf 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -475,6 +475,10 @@
       // `endpoint_result` came from A/AAAA records directly, without HTTPS/SVCB
       // records. If we know the QUIC ALPN to use externally, i.e. via Alt-Svc,
       // use it. Otherwise, `endpoint_result` is not eligible for QUIC.
+      //
+      // TODO(https://crbug.com/1287248): If all routes have ECH configs, and
+      // ECH is enabled, we should switch to a SVCB-reliant mode. See
+      // draft-ietf-dnsop-svcb-https-11, section 10.1.
       return quic_version_;
     }
 
@@ -2160,7 +2164,7 @@
       dns_resolution_end_time,
       std::make_unique<quic::QuicClientPushPromiseIndex>(), push_delegate_,
       tick_clock_, task_runner_, std::move(socket_performance_watcher),
-      net_log.net_log());
+      endpoint_result, net_log.net_log());
 
   all_sessions_[*session] = key;  // owning pointer
   writer->set_delegate(*session);
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 7fcf74e..d9392191 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -60,7 +60,7 @@
 #include "net/socket/socket_test_util.h"
 #include "net/spdy/spdy_session_test_util.h"
 #include "net/spdy/spdy_test_util_common.h"
-#include "net/ssl/ssl_config_service_defaults.h"
+#include "net/ssl/test_ssl_config_service.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
@@ -245,12 +245,14 @@
 
 class QuicStreamFactoryTestBase : public WithTaskEnvironment {
  protected:
-  QuicStreamFactoryTestBase(quic::ParsedQuicVersion version,
-                            bool enable_quic_priority_incremental_support)
+  QuicStreamFactoryTestBase(
+      quic::ParsedQuicVersion version,
+      bool enable_quic_priority_incremental_support,
+      std::vector<base::test::FeatureRef> enabled_features = {},
+      std::vector<base::test::FeatureRef> disabled_features = {})
       : host_resolver_(std::make_unique<MockHostResolver>(
             /*default_result=*/MockHostResolverBase::RuleResolver::
                 GetLocalhostResult())),
-        ssl_config_service_(std::make_unique<SSLConfigServiceDefaults>()),
         socket_factory_(std::make_unique<MockClientSocketFactory>()),
         runner_(base::MakeRefCounted<TestTaskRunner>(context_.mock_clock())),
         version_(version),
@@ -281,9 +283,12 @@
             &QuicStreamFactoryTestBase::OnFailedOnDefaultNetwork,
             base::Unretained(this))),
         quic_params_(context_.params()) {
-    scoped_feature_list_.InitWithFeatureState(
-        features::kPriorityIncremental,
-        enable_quic_priority_incremental_support);
+    if (enable_quic_priority_incremental_support) {
+      enabled_features.push_back(features::kPriorityIncremental);
+    } else {
+      disabled_features.push_back(features::kPriorityIncremental);
+    }
+    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
     FLAGS_quic_enable_http3_grease_randomness = false;
     context_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
   }
@@ -291,7 +296,7 @@
   void Initialize() {
     DCHECK(!factory_);
     factory_ = std::make_unique<QuicStreamFactory>(
-        net_log_.net_log(), host_resolver_.get(), ssl_config_service_.get(),
+        net_log_.net_log(), host_resolver_.get(), &ssl_config_service_,
         socket_factory_.get(), http_server_properties_.get(),
         cert_verifier_.get(), &ct_policy_enforcer_, &transport_security_state_,
         /*sct_auditing_delegate=*/nullptr,
@@ -959,7 +964,7 @@
 
   quic::test::QuicFlagSaver flags_;  // Save/restore all QUIC flag values.
   std::unique_ptr<MockHostResolverBase> host_resolver_;
-  std::unique_ptr<SSLConfigService> ssl_config_service_;
+  TestSSLConfigService ssl_config_service_{SSLContextConfig()};
   std::unique_ptr<MockClientSocketFactory> socket_factory_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
   MockQuicContext context_;
@@ -2425,7 +2430,7 @@
 TEST_P(QuicStreamFactoryTest, CloseSessionDuringCreation) {
   quic_params_->close_sessions_on_ip_change = true;
   auto factory = MockQuicStreamFactory(
-      net_log_.net_log(), host_resolver_.get(), ssl_config_service_.get(),
+      net_log_.net_log(), host_resolver_.get(), &ssl_config_service_,
       socket_factory_.get(), http_server_properties_.get(),
       cert_verifier_.get(), &ct_policy_enforcer_, &transport_security_state_,
       /*sct_auditing_delegate=*/nullptr,
@@ -15338,4 +15343,180 @@
   EXPECT_EQ(expected_dns_aliases2_, stream2->GetDnsAliases());
 }
 
+class QuicStreamFactoryEchTest : public QuicStreamFactoryTestBase,
+                                 public ::testing::TestWithParam<TestParams> {
+ protected:
+  QuicStreamFactoryEchTest()
+      : QuicStreamFactoryTestBase(
+            GetParam().version,
+            GetParam().enable_quic_priority_incremental_support,
+            /*enabled_features=*/
+            {features::kEncryptedClientHello,
+             features::kEncryptedClientHelloQuic}) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence,
+                         QuicStreamFactoryEchTest,
+                         ::testing::ValuesIn(GetTestParams()),
+                         ::testing::PrintToStringParamName());
+
+// Test that, even if DNS does not provide ECH keys, ECH GREASE is enabled.
+TEST_P(QuicStreamFactoryEchTest, EchGrease) {
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data(version_);
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
+                /*use_dns_aliases=*/true, /*require_dns_https_alpn=*/false,
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  ASSERT_TRUE(session);
+  quic::QuicSSLConfig config = session->GetSSLConfig();
+  EXPECT_TRUE(config.ech_grease_enabled);
+  EXPECT_TRUE(config.ech_config_list.empty());
+}
+
+// Test that, connections where we discover QUIC from Alt-Svc (as opposed to
+// HTTPS-RR), ECH is picked up from DNS.
+TEST_P(QuicStreamFactoryEchTest, EchWithQuicFromAltSvc) {
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = {IPEndPoint(IPAddress::IPv4Localhost(), 0)};
+  endpoint.metadata.supported_protocol_alpns = {quic::AlpnForVersion(version_)};
+  endpoint.metadata.ech_config_list = {1, 2, 3, 4};
+
+  host_resolver_ = std::make_unique<MockHostResolver>();
+  host_resolver_->rules()->AddRule(
+      scheme_host_port_.host(),
+      MockHostResolverBase::RuleResolver::RuleResult({endpoint}));
+
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data(version_);
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                scheme_host_port_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(), NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
+                /*use_dns_aliases=*/true, /*require_dns_https_alpn=*/false,
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  ASSERT_THAT(callback_.WaitForResult(), IsOk());
+
+  QuicChromiumClientSession* session = GetActiveSession(scheme_host_port_);
+  ASSERT_TRUE(session);
+  quic::QuicSSLConfig config = session->GetSSLConfig();
+  EXPECT_EQ(std::string(endpoint.metadata.ech_config_list.begin(),
+                        endpoint.metadata.ech_config_list.end()),
+            config.ech_config_list);
+}
+
+// Test that, connections where we discover QUIC from HTTPS-RR (as opposed to
+// Alt-Svc), ECH is picked up from DNS.
+TEST_P(QuicStreamFactoryEchTest, EchWithQuicFromHttpsRecord) {
+  quic_params_->supported_versions = {version_};
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = {IPEndPoint(IPAddress::IPv4Localhost(), 0)};
+  endpoint.metadata.supported_protocol_alpns = {quic::AlpnForVersion(version_)};
+  endpoint.metadata.ech_config_list = {1, 2, 3, 4};
+
+  host_resolver_ = std::make_unique<MockHostResolver>();
+  host_resolver_->rules()->AddRule(
+      scheme_host_port_.host(),
+      MockHostResolverBase::RuleResolver::RuleResult({endpoint}));
+
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data(version_);
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                scheme_host_port_, quic::ParsedQuicVersion::Unsupported(),
+                privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
+                /*use_dns_aliases=*/true, /*require_dns_https_alpn=*/true,
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  ASSERT_THAT(callback_.WaitForResult(), IsOk());
+
+  QuicChromiumClientSession* session =
+      GetActiveSession(scheme_host_port_, NetworkAnonymizationKey(),
+                       /*require_dns_https_alpn=*/true);
+  ASSERT_TRUE(session);
+  quic::QuicSSLConfig config = session->GetSSLConfig();
+  EXPECT_EQ(std::string(endpoint.metadata.ech_config_list.begin(),
+                        endpoint.metadata.ech_config_list.end()),
+            config.ech_config_list);
+}
+
+// Test that, when ECH is disabled, neither ECH nor ECH GREASE are configured.
+TEST_P(QuicStreamFactoryEchTest, EchDisabled) {
+  quic_params_->supported_versions = {version_};
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = {IPEndPoint(IPAddress::IPv4Localhost(), 0)};
+  endpoint.metadata.supported_protocol_alpns = {quic::AlpnForVersion(version_)};
+  endpoint.metadata.ech_config_list = {1, 2, 3, 4};
+
+  host_resolver_ = std::make_unique<MockHostResolver>();
+  host_resolver_->rules()->AddRule(
+      scheme_host_port_.host(),
+      MockHostResolverBase::RuleResolver::RuleResult({endpoint}));
+
+  SSLContextConfig ssl_config;
+  ssl_config.ech_enabled = false;
+  ssl_config_service_.UpdateSSLConfigAndNotify(ssl_config);
+
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data(version_);
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                scheme_host_port_, quic::ParsedQuicVersion::Unsupported(),
+                privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
+                NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
+                /*use_dns_aliases=*/true, /*require_dns_https_alpn=*/true,
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  ASSERT_THAT(callback_.WaitForResult(), IsOk());
+
+  QuicChromiumClientSession* session =
+      GetActiveSession(scheme_host_port_, NetworkAnonymizationKey(),
+                       /*require_dns_https_alpn=*/true);
+  ASSERT_TRUE(session);
+  quic::QuicSSLConfig config = session->GetSSLConfig();
+  EXPECT_TRUE(config.ech_config_list.empty());
+  EXPECT_FALSE(config.ech_grease_enabled);
+}
+
 }  // namespace net::test
diff --git a/net/socket/socket_options.cc b/net/socket/socket_options.cc
index 4b880c5..409fe5b 100644
--- a/net/socket/socket_options.cc
+++ b/net/socket/socket_options.cc
@@ -64,7 +64,9 @@
   int os_error = errno;
 #endif
   int net_error = (rv == -1) ? MapSystemError(os_error) : OK;
-  DCHECK(!rv) << "Could not set socket receive buffer size: " << net_error;
+  if (net_error != OK) {
+    DLOG(ERROR) << "Could not set socket receive buffer size: " << net_error;
+  }
   return net_error;
 }
 
@@ -77,7 +79,9 @@
   int os_error = errno;
 #endif
   int net_error = (rv == -1) ? MapSystemError(os_error) : OK;
-  DCHECK(!rv) << "Could not set socket receive buffer size: " << net_error;
+  if (net_error != OK) {
+    DLOG(ERROR) << "Could not set socket send buffer size: " << net_error;
+  }
   return net_error;
 }
 
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index 057e829..eee2955 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -6,10 +6,8 @@
 
 #include <string>
 
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/observer_list.h"
-#include "net/base/features.h"
 #include "net/socket/ssl_client_socket_impl.h"
 #include "net/socket/stream_socket.h"
 #include "net/ssl/ssl_client_session_cache.h"
@@ -78,11 +76,6 @@
   CertDatabase::GetInstance()->RemoveObserver(this);
 }
 
-bool SSLClientContext::EncryptedClientHelloEnabled() const {
-  return config_.ech_enabled &&
-         base::FeatureList::IsEnabled(features::kEncryptedClientHello);
-}
-
 std::unique_ptr<SSLClientSocket> SSLClientContext::CreateSSLClientSocket(
     std::unique_ptr<StreamSocket> stream_socket,
     const HostPortPair& host_and_port,
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index 8aade61a..a7a77a1 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -134,10 +134,6 @@
     return sct_auditing_delegate_;
   }
 
-  // Returns whether ECH (Encrypted ClientHello) should be enabled. This
-  // function checks both config() and feature flags.
-  bool EncryptedClientHelloEnabled() const;
-
   // Creates a new SSLClientSocket which can then be used to establish an SSL
   // connection to |host_and_port| over the already-connected |stream_socket|.
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 2d8c28d..a0af2fcf 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -916,11 +916,11 @@
         host_and_port_, &client_cert_, &client_private_key_);
   }
 
-  if (context_->EncryptedClientHelloEnabled()) {
+  if (context_->config().EncryptedClientHelloEnabled()) {
     SSL_set_enable_ech_grease(ssl_.get(), 1);
   }
   if (!ssl_config_.ech_config_list.empty()) {
-    DCHECK(context_->EncryptedClientHelloEnabled());
+    DCHECK(context_->config().EncryptedClientHelloEnabled());
     net_log_.AddEvent(NetLogEventType::SSL_ECH_CONFIG_LIST, [&] {
       base::Value::Dict dict;
       dict.Set("bytes", NetLogBinaryValue(ssl_config_.ech_config_list));
diff --git a/net/socket/ssl_connect_job.cc b/net/socket/ssl_connect_job.cc
index a5fe097..66889dd42 100644
--- a/net/socket/ssl_connect_job.cc
+++ b/net/socket/ssl_connect_job.cc
@@ -277,7 +277,7 @@
   absl::optional<TransportConnectJob::EndpointResultOverride>
       endpoint_result_override;
   if (ech_retry_configs_) {
-    DCHECK(ssl_client_context()->EncryptedClientHelloEnabled());
+    DCHECK(ssl_client_context()->config().EncryptedClientHelloEnabled());
     DCHECK(endpoint_result_);
     endpoint_result_override.emplace(*endpoint_result_, dns_aliases_);
   }
@@ -397,7 +397,7 @@
       disable_legacy_crypto_with_fallback_ ||
       !base::FeatureList::IsEnabled(features::kSHA1ServerSignature);
 
-  if (ssl_client_context()->EncryptedClientHelloEnabled()) {
+  if (ssl_client_context()->config().EncryptedClientHelloEnabled()) {
     if (ech_retry_configs_) {
       ssl_config.ech_config_list = *ech_retry_configs_;
     } else if (endpoint_result_) {
@@ -451,7 +451,7 @@
       endpoint_result_ && !endpoint_result_->metadata.ech_config_list.empty();
 
   if (!ech_retry_configs_ && result == ERR_ECH_NOT_NEGOTIATED &&
-      ssl_client_context()->EncryptedClientHelloEnabled()) {
+      ssl_client_context()->config().EncryptedClientHelloEnabled()) {
     // We used ECH, and the server could not decrypt the ClientHello. However,
     // it was able to handshake with the public name and send authenticated
     // retry configs. If this is not the first time around, retry the connection
diff --git a/net/ssl/ssl_config_service.cc b/net/ssl/ssl_config_service.cc
index c9ea712..97efb28 100644
--- a/net/ssl/ssl_config_service.cc
+++ b/net/ssl/ssl_config_service.cc
@@ -6,8 +6,9 @@
 
 #include <tuple>
 
+#include "base/feature_list.h"
 #include "base/observer_list.h"
-#include "net/ssl/ssl_config_service_defaults.h"
+#include "net/base/features.h"
 
 namespace net {
 
@@ -35,6 +36,11 @@
     default;
 SSLContextConfig& SSLContextConfig::operator=(SSLContextConfig&&) = default;
 
+bool SSLContextConfig::EncryptedClientHelloEnabled() const {
+  return ech_enabled &&
+         base::FeatureList::IsEnabled(features::kEncryptedClientHello);
+}
+
 SSLConfigService::SSLConfigService()
     : observer_list_(base::ObserverListPolicy::EXISTING_ONLY) {}
 
diff --git a/net/ssl/ssl_config_service.h b/net/ssl/ssl_config_service.h
index 219f8d1..c2f1526 100644
--- a/net/ssl/ssl_config_service.h
+++ b/net/ssl/ssl_config_service.h
@@ -21,6 +21,9 @@
   SSLContextConfig& operator=(const SSLContextConfig&);
   SSLContextConfig& operator=(SSLContextConfig&&);
 
+  // EncryptedClientHelloEnabled returns whether ECH is enabled.
+  bool EncryptedClientHelloEnabled() const;
+
   // The minimum and maximum protocol versions that are enabled.
   // (Use the SSL_PROTOCOL_VERSION_xxx enumerators defined in ssl_config.h.)
   // SSL 2.0/3.0 and TLS 1.0/1.1 are not supported. If version_max <
@@ -42,10 +45,11 @@
   bool cecpq2_enabled = true;
 
   // If false, disables TLS Encrypted ClientHello (ECH). If true, the feature
-  // may be enabled or disabled, depending on feature flags.
+  // may be enabled or disabled, depending on feature flags. If querying whether
+  // ECH is enabled, use `EncryptedClientHelloEnabled` instead.
   bool ech_enabled = true;
 
-  // ADDING MORE HERE? Don't forget to update |SSLContextConfigsAreEqual|.
+  // ADDING MORE HERE? Don't forget to update `SSLContextConfigsAreEqual`.
 };
 
 // The interface for retrieving global SSL configuration.  This interface
diff --git a/net/websockets/websocket_basic_stream_adapters_test.cc b/net/websockets/websocket_basic_stream_adapters_test.cc
index f91b13f..908e65c4 100644
--- a/net/websockets/websocket_basic_stream_adapters_test.cc
+++ b/net/websockets/websocket_basic_stream_adapters_test.cc
@@ -21,6 +21,7 @@
 #include "net/base/proxy_server.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_network_session.h"
 #include "net/log/net_log_with_source.h"
@@ -39,13 +40,13 @@
 #include "net/socket/connect_job.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
-#include "net/socket/ssl_client_socket.h"
 #include "net/socket/transport_client_socket_pool.h"
 #include "net/socket/websocket_endpoint_lock_manager.h"
 #include "net/spdy/spdy_session.h"
 #include "net/spdy/spdy_session_key.h"
 #include "net/spdy/spdy_test_util_common.h"
 #include "net/ssl/ssl_config.h"
+#include "net/ssl/ssl_config_service_defaults.h"
 #include "net/ssl/ssl_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
@@ -1222,7 +1223,7 @@
     session_ = std::make_unique<QuicChromiumClientSession>(
         connection, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_, /*ssl_config_service=*/nullptr,
+        &transport_security_state_, &ssl_config_service_,
         /*server_info=*/nullptr,
         QuicSessionKey("mail.example.org", 80, PRIVACY_MODE_DISABLED,
                        SocketTag(), NetworkAnonymizationKey(),
@@ -1247,7 +1248,8 @@
         std::make_unique<quic::QuicClientPushPromiseIndex>(), nullptr,
         base::DefaultTickClock::GetInstance(),
         base::SingleThreadTaskRunner::GetCurrentDefault().get(),
-        /*socket_performance_watcher=*/nullptr, NetLog::Get());
+        /*socket_performance_watcher=*/nullptr, HostResolverEndpointResult(),
+        NetLog::Get());
 
     session_->Initialize();
 
@@ -1281,6 +1283,7 @@
   scoped_refptr<TestTaskRunner> runner_;
   ProofVerifyDetailsChromium verify_details_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
+  SSLConfigServiceDefaults ssl_config_service_;
   quic::test::MockConnectionIdGenerator connection_id_generator_;
   std::unique_ptr<QuicChromiumConnectionHelper> helper_;
   std::unique_ptr<QuicChromiumAlarmFactory> alarm_factory_;
diff --git a/net/websockets/websocket_handshake_stream_create_helper_test.cc b/net/websockets/websocket_handshake_stream_create_helper_test.cc
index 8587eebb..a3e6f0df 100644
--- a/net/websockets/websocket_handshake_stream_create_helper_test.cc
+++ b/net/websockets/websocket_handshake_stream_create_helper_test.cc
@@ -17,6 +17,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/privacy_mode.h"
 #include "net/base/proxy_server.h"
+#include "net/dns/public/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_request_headers.h"
@@ -37,13 +38,11 @@
 #include "net/socket/connect_job.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
-#include "net/socket/ssl_client_socket.h"
 #include "net/socket/websocket_endpoint_lock_manager.h"
 #include "net/spdy/spdy_session.h"
 #include "net/spdy/spdy_session_key.h"
 #include "net/spdy/spdy_test_util_common.h"
-#include "net/ssl/ssl_config.h"
-#include "net/ssl/ssl_info.h"
+#include "net/ssl/ssl_config_service_defaults.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_data_directory.h"
@@ -346,6 +345,7 @@
         ProofVerifyDetailsChromium verify_details;
         MockCryptoClientStreamFactory crypto_client_stream_factory;
         TransportSecurityState transport_security_state;
+        SSLConfigServiceDefaults ssl_config_service;
 
         FLAGS_quic_enable_http3_grease_randomness = false;
         clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(20));
@@ -425,7 +425,7 @@
         session_ = std::make_unique<QuicChromiumClientSession>(
             connection, std::move(socket),
             /*stream_factory=*/nullptr, &crypto_client_stream_factory, &clock_,
-            &transport_security_state, /*ssl_config_service=*/nullptr,
+            &transport_security_state, &ssl_config_service,
             /*server_info=*/nullptr,
             QuicSessionKey("mail.example.org", 80, PRIVACY_MODE_DISABLED,
                            SocketTag(), NetworkAnonymizationKey(),
@@ -450,7 +450,8 @@
             std::make_unique<quic::QuicClientPushPromiseIndex>(), nullptr,
             base::DefaultTickClock::GetInstance(),
             base::SingleThreadTaskRunner::GetCurrentDefault().get(),
-            /*socket_performance_watcher=*/nullptr, NetLog::Get());
+            /*socket_performance_watcher=*/nullptr,
+            HostResolverEndpointResult(), NetLog::Get());
 
         session_->Initialize();
 
diff --git a/remoting/test/it2me_cli_host.cc b/remoting/test/it2me_cli_host.cc
index 3eff8505..bf71356b 100644
--- a/remoting/test/it2me_cli_host.cc
+++ b/remoting/test/it2me_cli_host.cc
@@ -96,48 +96,45 @@
     return;
   }
 
-  auto* type_value =
-      message_value->FindKeyOfType(kMessageType, base::Value::Type::STRING);
-  if (!type_value) {
+  base::Value::Dict& message_dict = message_value->GetDict();
+  std::string* type = message_dict.FindString(kMessageType);
+  if (!type) {
     OnProtocolBroken("Message without type");
     return;
   }
-  std::string type = type_value->GetString();
 
-  if (type == kHelloResponse) {
+  if (*type == kHelloResponse) {
     OnHelloResponse();
-  } else if (type == kConnectResponse) {
+  } else if (*type == kConnectResponse) {
     // Ok, just ignore.
-  } else if (type == kDisconnectResponse) {
+  } else if (*type == kDisconnectResponse) {
     OnDisconnectResponse();
-  } else if (type == kHostStateChangedMessage) {
+  } else if (*type == kHostStateChangedMessage) {
     // Handle CRD host state changes
-    auto* state_value =
-        message_value->FindKeyOfType(kState, base::Value::Type::STRING);
-    if (!state_value) {
+    std::string* state = message_dict.FindString(kState);
+    if (!state) {
       OnProtocolBroken("No state in message");
       return;
     }
-    std::string state = state_value->GetString();
 
-    if (state == kHostStateReceivedAccessCode) {
-      OnStateReceivedAccessCode(*message_value);
-    } else if (state == kHostStateConnected) {
-      OnStateRemoteConnected(*message_value);
-    } else if (state == kHostStateDisconnected) {
+    if (*state == kHostStateReceivedAccessCode) {
+      OnStateReceivedAccessCode(message_dict);
+    } else if (*state == kHostStateConnected) {
+      OnStateRemoteConnected(message_dict);
+    } else if (*state == kHostStateDisconnected) {
       OnStateRemoteDisconnected();
-    } else if (state == kHostStateError || state == kHostStateDomainError) {
-      OnStateError(state, *message_value);
-    } else if (state == kHostStateStarting ||
-               state == kHostStateRequestedAccessCode) {
+    } else if (*state == kHostStateError || *state == kHostStateDomainError) {
+      OnStateError(*state, message_dict);
+    } else if (*state == kHostStateStarting ||
+               *state == kHostStateRequestedAccessCode) {
       // Just ignore these states.
     } else {
-      LOG(WARNING) << "Unhandled state: " << state;
+      LOG(WARNING) << "Unhandled state: " << *state;
     }
-  } else if (type == kCRDDebugLog) {
+  } else if (*type == kCRDDebugLog) {
     // The It2Me host already prints the log to stdout/stderr.
   } else {
-    LOG(WARNING) << "Unknown message type: " << type;
+    LOG(WARNING) << "Unknown message type: " << *type;
   }
 }
 
@@ -226,15 +223,14 @@
 }
 
 void It2MeCliHost::OnStateError(const std::string& error_state,
-                                const base::Value& message) {
+                                const base::Value::Dict& message) {
   std::string error_message;
   if (error_state == kHostStateDomainError) {
     error_message = "CRD Error : Invalid domain";
   } else {
-    auto* error_code_value =
-        message.FindKeyOfType(kErrorMessageCode, base::Value::Type::STRING);
-    if (error_code_value) {
-      error_message = error_code_value->GetString();
+    const std::string* error_code = message.FindString(kErrorMessageCode);
+    if (error_code) {
+      error_message = *error_code;
     } else {
       error_message = "Unknown CRD Error";
     }
@@ -248,12 +244,11 @@
   ShutdownHost();
 }
 
-void It2MeCliHost::OnStateRemoteConnected(const base::Value& message) {
+void It2MeCliHost::OnStateRemoteConnected(const base::Value::Dict& message) {
   remote_connected_ = true;
-  auto* client_value =
-      message.FindKeyOfType(kClient, base::Value::Type::STRING);
-  if (client_value) {
-    HOST_LOG << "Remote connection by " << client_value->GetString();
+  const std::string* client = message.FindString(kClient);
+  if (client) {
+    HOST_LOG << "Remote connection by " << *client;
   }
 }
 
@@ -270,7 +265,7 @@
   SendMessageToHost(kDisconnectMessage, std::move(params));
 }
 
-void It2MeCliHost::OnStateReceivedAccessCode(const base::Value& message) {
+void It2MeCliHost::OnStateReceivedAccessCode(const base::Value::Dict& message) {
   if (!command_awaiting_crd_access_code_) {
     if (!remote_connected_) {
       // We have already sent the access code back to the server which initiated
@@ -283,19 +278,18 @@
     return;
   }
 
-  auto* code_value =
-      message.FindKeyOfType(kAccessCode, base::Value::Type::STRING);
-  auto* code_lifetime_value =
-      message.FindKeyOfType(kAccessCodeLifetime, base::Value::Type::INTEGER);
-  if (!code_value || !code_lifetime_value) {
+  const std::string* code = message.FindString(kAccessCode);
+  const absl::optional<int> code_lifetime =
+      message.FindInt(kAccessCodeLifetime);
+  if (!code || !code_lifetime) {
     OnProtocolBroken("Can not obtain access code");
     return;
   }
   command_awaiting_crd_access_code_ = false;
 
   // Prints the access code.
-  base::TimeDelta expires_in = base::Seconds(code_lifetime_value->GetInt());
-  HOST_LOG << "It2Me access code is generated: " << code_value->GetString();
+  base::TimeDelta expires_in = base::Seconds(*code_lifetime);
+  HOST_LOG << "It2Me access code is generated: " << *code;
   HOST_LOG << "Expires at: " << (base::Time::Now() + expires_in);
 }
 
diff --git a/remoting/test/it2me_cli_host.h b/remoting/test/it2me_cli_host.h
index 05502b5..733e934 100644
--- a/remoting/test/it2me_cli_host.h
+++ b/remoting/test/it2me_cli_host.h
@@ -62,10 +62,11 @@
   void OnHelloResponse();
   void OnDisconnectResponse();
 
-  void OnStateError(const std::string& error_state, const base::Value& message);
-  void OnStateRemoteConnected(const base::Value& message);
+  void OnStateError(const std::string& error_state,
+                    const base::Value::Dict& message);
+  void OnStateRemoteConnected(const base::Value::Dict& message);
   void OnStateRemoteDisconnected();
-  void OnStateReceivedAccessCode(const base::Value& message);
+  void OnStateReceivedAccessCode(const base::Value::Dict& message);
 
   std::unique_ptr<test::TestTokenStorage> storage_;
   std::unique_ptr<test::TestOAuthTokenGetter> token_getter_;
diff --git a/sandbox/mac/seatbelt_extension_unittest.cc b/sandbox/mac/seatbelt_extension_unittest.cc
index 6e11c74..e0923130 100644
--- a/sandbox/mac/seatbelt_extension_unittest.cc
+++ b/sandbox/mac/seatbelt_extension_unittest.cc
@@ -31,8 +31,6 @@
 )";
 
 const char kTestData[] = "hello world";
-constexpr int kTestDataLen = std::size(kTestData);
-
 const char kSwitchFile[] = "test-file";
 const char kSwitchExtension[] = "test-extension";
 
@@ -42,8 +40,7 @@
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     file_path_ = temp_dir_.GetPath().AppendASCII("sbox.test");
 
-    ASSERT_EQ(kTestDataLen,
-              base::WriteFile(file_path_, kTestData, kTestDataLen));
+    ASSERT_TRUE(base::WriteFile(file_path_, kTestData));
   }
 
   base::FilePath file_path() { return file_path_; }
diff --git a/services/device/geolocation/win/location_provider_winrt.cc b/services/device/geolocation/win/location_provider_winrt.cc
index 86752d8..81dff79 100644
--- a/services/device/geolocation/win/location_provider_winrt.cc
+++ b/services/device/geolocation/win/location_provider_winrt.cc
@@ -208,10 +208,6 @@
       event = WindowsRTLocationRequestEvent::
           WINDOWS_RT_LOCATION_CALLBACK_EVENT_POSITION_UNAVAILABLE;
       break;
-    case mojom::Geoposition::ErrorCode::TIMEOUT:
-      event = WindowsRTLocationRequestEvent::
-          WINDOWS_RT_LOCATION_CALLBACK_EVENT_TIMEOUT;
-      break;
     default:
       event = WindowsRTLocationRequestEvent::
           WINDOWS_RT_LOCATION_CALLBACK_EVENT_UNKNOWN_ERROR_CONDITION;
diff --git a/services/device/public/mojom/geoposition.mojom b/services/device/public/mojom/geoposition.mojom
index be6f9c3..fcfbd39 100644
--- a/services/device/public/mojom/geoposition.mojom
+++ b/services/device/public/mojom/geoposition.mojom
@@ -25,8 +25,7 @@
     NONE = 0,  // Chrome addition.
     PERMISSION_DENIED = 1,
     POSITION_UNAVAILABLE = 2,
-    TIMEOUT = 3,
-    LAST = TIMEOUT
+    LAST = POSITION_UNAVAILABLE
   };
 
   // Whether this geoposition is valid.
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index e8ec68a..0938ee0 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5818,9 +5818,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5832,8 +5832,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -5989,9 +5989,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6003,8 +6003,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -6141,9 +6141,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6155,8 +6155,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 11a6ea5..9c137e2 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -19408,7 +19408,8 @@
       },
       {
         "args": [
-          "--extra-browser-args=--enable-crashpad"
+          "--extra-browser-args=--enable-crashpad",
+          "--xvfb"
         ],
         "isolate_name": "telemetry_perf_unittests",
         "isolate_profile_data": true,
@@ -20486,9 +20487,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -20500,8 +20501,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -20657,9 +20658,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -20671,8 +20672,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -20809,9 +20810,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -20823,8 +20824,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index d672332..d636da7f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -408,7 +408,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 20
         },
         "test": "browser_tests",
         "test_id_prefix": "ninja://chrome/test:browser_tests/"
@@ -48776,9 +48776,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48789,8 +48789,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -48947,9 +48947,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48960,8 +48960,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -49099,9 +49099,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -49112,8 +49112,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -50637,9 +50637,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -50650,8 +50650,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -50808,9 +50808,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -50821,8 +50821,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -50960,9 +50960,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -50973,8 +50973,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -51746,9 +51746,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -51759,8 +51759,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 97c072d..b9f43a0 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -18533,12 +18533,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18550,8 +18550,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -18724,12 +18724,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18741,8 +18741,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
@@ -18891,12 +18891,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 113.0.5620.0",
+        "description": "Run with ash-chrome version 113.0.5621.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18908,8 +18908,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5620.0",
-              "revision": "version:113.0.5620.0"
+              "location": "lacros_version_skew_tests_v113.0.5621.0",
+              "revision": "version:113.0.5621.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
index 0e993393..76a0296 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
@@ -23,11 +23,13 @@
 -All/SAMLDeviceAttestationEnrolledTest.*
 -All/SAMLDeviceAttestationTest.*
 -All/SAMLEnrollmentTest.*
+-All/SAMLLocalesTest.*
 -All/SAMLPasswordAttributesTest.*
 -All/SAMLPolicyTest.*
 -All/SamlTestWithFeatures.*
 -All/SmartPrivacyProtectionScreenTest.*
 -All/SshWarningTest.*
+-All/SsoProfileTest.*
 -All/SyncConsentPolicyDisabledTest.*
 -All/SyncConsentTestWithModesParams.*
 -All/SyncConsentTestWithParams.*
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
index 8d73510..a4d862c 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
@@ -23,11 +23,13 @@
 All/SAMLDeviceAttestationEnrolledTest.*
 All/SAMLDeviceAttestationTest.*
 All/SAMLEnrollmentTest.*
+All/SAMLLocalesTest.*
 All/SAMLPasswordAttributesTest.*
 All/SAMLPolicyTest.*
 All/SamlTestWithFeatures.*
 All/SmartPrivacyProtectionScreenTest.*
 All/SshWarningTest.*
+All/SsoProfileTest.*
 All/SyncConsentPolicyDisabledTest.*
 All/SyncConsentTestWithModesParams.*
 All/SyncConsentTestWithParams.*
diff --git a/testing/buildbot/filters/pixel_tests.filter b/testing/buildbot/filters/pixel_tests.filter
index 3ad131fd..0975865b 100644
--- a/testing/buildbot/filters/pixel_tests.filter
+++ b/testing/buildbot/filters/pixel_tests.filter
@@ -87,7 +87,4 @@
 # # TODO(crbug.com/xxx): Fix xyz and re-enable.
 # -MyTestSuite.InvokeUi_MyTestCase
 
-# This test uses random network port and shows it on ui.
--ContentSettingBubbleDialogTest.InvokeUi_popups
-
 -OutdatedUpgradeBubbleTest.InvokeUi_Critical
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 4cf9442..c049de43 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1039,6 +1039,11 @@
         ],
         'experiment_percentage': 100, # https://crbug.com/1251657
       },
+      'Mac Builder Next': {
+        'swarming': {
+          'shards': 20,  # crbug.com/1419045
+        },
+      },
       'Mac10.15 Tests': {
         # crbug.com/1042757
         'swarming': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 02b06d5..090e12ba 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5026,6 +5026,23 @@
       },
     },
 
+    'telemetry_perf_unittests_isolated_scripts_xvfb': {
+      'telemetry_perf_unittests': {
+        'args': [
+          # TODO(crbug.com/1077284): Remove this once Crashpad is the default.
+          '--extra-browser-args=--enable-crashpad',
+          '--xvfb',
+        ],
+        'swarming': {
+          'idempotent': False,  # https://crbug.com/549140
+          'shards': 12,
+        },
+        'resultdb': {
+          'enable': True,
+        },
+      },
+    },
+
     'test_buildbucket_api_gpu_use_cases': {
       'test_buildbucket_api_gpu_use_cases': {},
     },
@@ -5957,7 +5974,7 @@
       'linux_specific_chromium_isolated_scripts',
       'mojo_python_unittests_isolated_scripts',
       'pytype_tests',
-      'telemetry_perf_unittests_isolated_scripts',
+      'telemetry_perf_unittests_isolated_scripts_xvfb',
       'vulkan_swiftshader_isolated_scripts',
       'chromium_web_tests_high_dpi_isolated_scripts',
       'gpu_dawn_webgpu_blink_web_tests',
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 0eeccd5..c2d3374 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5620.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5621.0/test_ash_chrome',
     ],
-    'description': 'Run with ash-chrome version 113.0.5620.0',
+    'description': 'Run with ash-chrome version 113.0.5621.0',
     'identifier': 'Lacros version skew testing ash canary',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v113.0.5620.0',
-          'revision': 'version:113.0.5620.0',
+          'location': 'lacros_version_skew_tests_v113.0.5621.0',
+          'revision': 'version:113.0.5621.0',
         },
       ],
     },
diff --git a/testing/libfuzzer/BUILD.gn b/testing/libfuzzer/BUILD.gn
index 669ea45..6f0c482 100644
--- a/testing/libfuzzer/BUILD.gn
+++ b/testing/libfuzzer/BUILD.gn
@@ -43,7 +43,15 @@
 # The currently selected fuzzing engine, providing a main() function.
 # Fuzzers should depend upon this.
 group("fuzzing_engine_main") {
-  deps = [ ":libfuzzer_main" ]
+  deps = [
+    ":fuzzing_engine",
+    ":libfuzzer_main",
+  ]
+}
+
+# Any fuzzer using any fuzzing engine. This will be used by infra scripts
+# to identify fuzzers which should be built and made available to ClusterFuzz.
+group("fuzzing_engine") {
 }
 
 # A config used by all fuzzer_tests.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 15b140c..c015389a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -132,27 +132,6 @@
             ]
         }
     ],
-    "AnchorElementInteractionPreloader": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20220428",
-                    "enable_features": [
-                        "AnchorElementInteraction"
-                    ]
-                }
-            ]
-        }
-    ],
     "AndroidBackPressRefactor": [
         {
             "platforms": [
@@ -1038,6 +1017,27 @@
             ]
         }
     ],
+    "AutofillAlwaysParsePlaceholders": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillAlwaysParsePlaceholders"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillEnableLabelPrecedenceForTurkishAddresses": [
         {
             "platforms": [
@@ -5263,35 +5263,6 @@
             ]
         }
     ],
-    "FullscreenPromosManager": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled (Fullscreen)",
-                    "params": {
-                        "ios-new-post-restore-experience": "false"
-                    },
-                    "enable_features": [
-                        "FullscreenPromosManager",
-                        "IOSNewPostRestoreExperience"
-                    ]
-                },
-                {
-                    "name": "Enabled (Alert)",
-                    "params": {
-                        "ios-new-post-restore-experience": "true"
-                    },
-                    "enable_features": [
-                        "FullscreenPromosManager",
-                        "IOSNewPostRestoreExperience"
-                    ]
-                }
-            ]
-        }
-    ],
     "GMSCoreEmoji": [
         {
             "platforms": [
@@ -7366,6 +7337,21 @@
             ]
         }
     ],
+    "MacSetDefaultTaskRole": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MacSetDefaultTaskRole"
+                    ]
+                }
+            ]
+        }
+    ],
     "MacWebContentsOcclusion": [
         {
             "platforms": [
@@ -7402,6 +7388,7 @@
                 "chromeos",
                 "chromeos_lacros",
                 "fuchsia",
+                "ios",
                 "linux",
                 "mac",
                 "windows"
@@ -11590,6 +11577,7 @@
                 {
                     "name": "Enabled",
                     "enable_features": [
+                        "DeferNotifyInMotion",
                         "SuppressToolbarCaptures",
                         "UpdateBrowserControlsWithoutProxy"
                     ]
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 2934bcd..cb72f99 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -639,6 +639,10 @@
                 sb.append('  # https://crbug.com/989505\n')
                 sb.append('  jar_excluded_patterns = ["META-INF/proguard/*"]\n')
                 break
+            case 'androidx_benchmark_benchmark_macro':
+                // Manually add dep onto DISALLOWED_DEP androidx.profileinstaller.
+                sb.append('  deps += [ ":androidx_profileinstaller_profileinstaller_java" ]\n')
+                break
             case 'androidx_core_core':
                 sb.with {
                     append('\n')
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index 2c343ed..2c44b49 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -299,6 +299,7 @@
         // Bill of materials (BOM) deps are used to specify versions for other dependencies and don't have children or
         // artifacts of their own. Add other such empty deps here when we encounter them.
         'org_jetbrains_kotlinx_kotlinx_coroutines_bom',
+        'com_squareup_okio_okio_bom',
     ] as Set
 
     // Local text versions of HTML licenses. This cannot replace PROPERTY_OVERRIDES because some libraries refer to
diff --git a/third_party/androidx/build.gradle.template b/third_party/androidx/build.gradle.template
index be3b73b..01c5de1 100644
--- a/third_party/androidx/build.gradle.template
+++ b/third_party/androidx/build.gradle.template
@@ -94,11 +94,13 @@
     compile 'androidx.legacy:legacy-support-v4:1.0.0'
 
     // testCompile targets have test_only = true.
+    androidTestCompile 'androidx.benchmark:benchmark-macro-junit4:{{androidx_dependency_version}}'
+    androidTestCompile 'androidx.benchmark:benchmark-junit4:{{androidx_dependency_version}}'
     androidTestCompile 'androidx.fragment:fragment-testing:{{androidx_dependency_version}}'
     androidTestCompile 'androidx.test:core:1.4.0-rc01'
     androidTestCompile 'androidx.test:monitor:1.4.0-rc01'
     androidTestCompile 'androidx.test:rules:1.2.0'
-    androidTestCompile 'androidx.test:runner:1.2.0'
+    androidTestCompile 'androidx.test:runner:1.4.0'
     androidTestCompile 'androidx.test.espresso:espresso-contrib:3.2.0'
     androidTestCompile 'androidx.test.espresso:espresso-core:3.2.0'
     androidTestCompile 'androidx.test.espresso:espresso-intents:3.2.0'
diff --git a/third_party/blink/common/interest_group/interest_group_mojom_traits.cc b/third_party/blink/common/interest_group/interest_group_mojom_traits.cc
index deb96d8..73cfd88 100644
--- a/third_party/blink/common/interest_group/interest_group_mojom_traits.cc
+++ b/third_party/blink/common/interest_group/interest_group_mojom_traits.cc
@@ -34,13 +34,13 @@
 }
 
 bool StructTraits<blink::mojom::SellerCapabilitiesDataView,
-                  blink::InterestGroup::SellerCapabilitiesType>::
+                  blink::SellerCapabilitiesType>::
     Read(blink::mojom::SellerCapabilitiesDataView data,
-         blink::InterestGroup::SellerCapabilitiesType* out) {
+         blink::SellerCapabilitiesType* out) {
   if (data.allows_interest_group_counts())
-    out->Put(blink::InterestGroup::SellerCapabilities::kInterestGroupCounts);
+    out->Put(blink::SellerCapabilities::kInterestGroupCounts);
   if (data.allows_latency_stats())
-    out->Put(blink::InterestGroup::SellerCapabilities::kLatencyStats);
+    out->Put(blink::SellerCapabilities::kLatencyStats);
   return true;
 }
 
diff --git a/third_party/blink/common/interest_group/interest_group_mojom_traits_test.cc b/third_party/blink/common/interest_group/interest_group_mojom_traits_test.cc
index 461ff31..5f214e10 100644
--- a/third_party/blink/common/interest_group/interest_group_mojom_traits_test.cc
+++ b/third_party/blink/common/interest_group/interest_group_mojom_traits_test.cc
@@ -134,17 +134,17 @@
   InterestGroup interest_group = CreateInterestGroup();
 
   interest_group.all_sellers_capabilities.Put(
-      InterestGroup::SellerCapabilities::kInterestGroupCounts);
+      SellerCapabilities::kInterestGroupCounts);
   SerializeAndDeserializeAndCompare(interest_group);
 
   interest_group.all_sellers_capabilities.Put(
-      InterestGroup::SellerCapabilities::kLatencyStats);
+      SellerCapabilities::kLatencyStats);
   SerializeAndDeserializeAndCompare(interest_group);
 
   interest_group.all_sellers_capabilities.Put(
-      InterestGroup::SellerCapabilities::kInterestGroupCounts);
+      SellerCapabilities::kInterestGroupCounts);
   interest_group.all_sellers_capabilities.Put(
-      InterestGroup::SellerCapabilities::kLatencyStats);
+      SellerCapabilities::kLatencyStats);
   SerializeAndDeserializeAndCompare(interest_group);
 }
 
diff --git a/third_party/blink/common/interest_group/test_interest_group_builder.cc b/third_party/blink/common/interest_group/test_interest_group_builder.cc
index 0f6a64b..38178e0 100644
--- a/third_party/blink/common/interest_group/test_interest_group_builder.cc
+++ b/third_party/blink/common/interest_group/test_interest_group_builder.cc
@@ -86,15 +86,14 @@
 }
 
 TestInterestGroupBuilder& TestInterestGroupBuilder::SetSellerCapabilities(
-    absl::optional<
-        base::flat_map<url::Origin, InterestGroup::SellerCapabilitiesType>>
+    absl::optional<base::flat_map<url::Origin, SellerCapabilitiesType>>
         seller_capabilities) {
   interest_group_.seller_capabilities = std::move(seller_capabilities);
   return *this;
 }
 
 TestInterestGroupBuilder& TestInterestGroupBuilder::SetAllSellerCapabilities(
-    InterestGroup::SellerCapabilitiesType all_sellers_capabilities) {
+    SellerCapabilitiesType all_sellers_capabilities) {
   interest_group_.all_sellers_capabilities =
       std::move(all_sellers_capabilities);
   return *this;
diff --git a/third_party/blink/common/storage_key/storage_key.cc b/third_party/blink/common/storage_key/storage_key.cc
index 141dda6f..964476f 100644
--- a/third_party/blink/common/storage_key/storage_key.cc
+++ b/third_party/blink/common/storage_key/storage_key.cc
@@ -140,7 +140,8 @@
     }
 
     return StorageKey(key_origin, key_top_level_site, nullptr,
-                      blink::mojom::AncestorChainBit::kSameSite);
+                      blink::mojom::AncestorChainBit::kSameSite,
+                      /*third_party_partitioning_allowed=*/false);
   }
 
   if (!ValidSeparatorWithData(in, pos_first_caret))
@@ -199,8 +200,15 @@
 
       // The ancestor chain bit must be CrossSite as that's an invariant
       // when the origin and top level site don't match.
+      // TODO(crbug.com/1199077): Deserialize should always be able to make 3p
+      // keys and shouldn't depend on the state of partitioning (because we
+      // don't want to inadvertently turn two 3p keys into the same 1p key).
+      // Unfortunately, some tests (and potentially code) depend on this. Here,
+      // and below, should be changed to true and the dependencies on this
+      // behavior should be removed.
       return StorageKey(key_origin, key_top_level_site, nullptr,
-                        blink::mojom::AncestorChainBit::kCrossSite);
+                        blink::mojom::AncestorChainBit::kCrossSite,
+                        IsThirdPartyStoragePartitioningEnabled());
     }
     case EncodedAttribute::kAncestorChainBit: {
       // Same-Origin kCrossSite keys cannot be read if partitioning is off.
@@ -248,7 +256,8 @@
 
       // This format indicates the top level site matches the origin.
       return StorageKey(key_origin, net::SchemefulSite(key_origin), nullptr,
-                        ancestor_chain_bit);
+                        ancestor_chain_bit,
+                        IsThirdPartyStoragePartitioningEnabled());
     }
     case EncodedAttribute::kNonceHigh: {
       // A nonce is serialized and has only two encoded attributes.
@@ -296,6 +305,12 @@
       if (!base::StringToUint64(low_digits, &nonce_low))
         return absl::nullopt;
 
+      // The key is corrupted if there are extra 0s in front of the nonce.
+      if (base::NumberToString(nonce_high) != high_digits ||
+          base::NumberToString(nonce_low) != low_digits) {
+        return absl::nullopt;
+      }
+
       nonce = base::UnguessableToken::Deserialize(nonce_high, nonce_low);
 
       if (!nonce.has_value()) {
@@ -304,9 +319,12 @@
 
       // This constructor makes a copy of the nonce, so getting the raw pointer
       // is safe.
+      // Note: The partitioning allowed value is irrelevant with a nonce,
+      // `false` was chosen arbitrarily.
       return StorageKey(key_origin, net::SchemefulSite(key_origin),
                         &nonce.value(),
-                        blink::mojom::AncestorChainBit::kCrossSite);
+                        blink::mojom::AncestorChainBit::kCrossSite,
+                        /*third_party_partitioning_allowed=*/false);
     }
     case EncodedAttribute::kTopLevelSiteOpaqueNonceHigh: {
       // An opaque `top_level_site` is serialized.
@@ -363,6 +381,12 @@
         return absl::nullopt;
       }
 
+      // The key is corrupted if there are extra 0s in front of the nonce.
+      if (base::NumberToString(nonce_high) != high_digits ||
+          base::NumberToString(nonce_low) != low_digits) {
+        return absl::nullopt;
+      }
+
       const absl::optional<base::UnguessableToken> site_nonce =
           base::UnguessableToken::Deserialize(nonce_high, nonce_low);
 
@@ -405,7 +429,8 @@
           key_origin,
           net::SchemefulSite(url::Origin(url::Origin::Nonce(site_nonce.value()),
                                          tuple_precursor)),
-          nullptr, blink::mojom::AncestorChainBit::kCrossSite);
+          nullptr, blink::mojom::AncestorChainBit::kCrossSite,
+          IsThirdPartyStoragePartitioningEnabled());
     }
     default: {
       // Malformed input case. We saw a separator that we don't understand
@@ -424,7 +449,8 @@
   if (!maybe_origin.opaque()) {
     if (maybe_origin.Serialize() == in) {
       return StorageKey(maybe_origin, net::SchemefulSite(maybe_origin), nullptr,
-                        blink::mojom::AncestorChainBit::kSameSite);
+                        blink::mojom::AncestorChainBit::kSameSite,
+                        /*third_party_partitioning_allowed=*/false);
     } else if (maybe_origin.GetURL().spec() == in) {
       // This first party key was passed in with a trailing slash. This is
       // required in Deserialize() but improper for DeserializeForLocalStorage()
@@ -479,9 +505,9 @@
 // static
 StorageKey StorageKey::CreateFirstParty(const url::Origin& origin) {
   return StorageKey(origin, net::SchemefulSite(origin), nullptr,
-                    origin.opaque()
-                        ? blink::mojom::AncestorChainBit::kCrossSite
-                        : blink::mojom::AncestorChainBit::kSameSite);
+                    origin.opaque() ? blink::mojom::AncestorChainBit::kCrossSite
+                                    : blink::mojom::AncestorChainBit::kSameSite,
+                    /*third_party_partitioning_allowed=*/false);
 }
 
 // static
@@ -489,16 +515,20 @@
                                        const base::UnguessableToken& nonce) {
   // The AncestorChainBit is not applicable to StorageKeys with a non-empty
   // nonce, so they are initialized to be kCrossSite.
+  // Note: The partitioning allowed value is irrelevant with a nonce, `false`
+  // was chosen arbitrarily.
   return StorageKey(origin, net::SchemefulSite(origin), &nonce,
-                    blink::mojom::AncestorChainBit::kCrossSite);
+                    blink::mojom::AncestorChainBit::kCrossSite,
+                    /*third_party_partitioning_allowed=*/false);
 }
 
 // static
-StorageKey StorageKey::Create(
-    const url::Origin& origin,
-    const net::SchemefulSite& top_level_site,
-    blink::mojom::AncestorChainBit ancestor_chain_bit) {
-  return StorageKey(origin, top_level_site, nullptr, ancestor_chain_bit);
+StorageKey StorageKey::Create(const url::Origin& origin,
+                              const net::SchemefulSite& top_level_site,
+                              blink::mojom::AncestorChainBit ancestor_chain_bit,
+                              bool third_party_partitioning_allowed) {
+  return StorageKey(origin, top_level_site, nullptr, ancestor_chain_bit,
+                    third_party_partitioning_allowed);
 }
 
 // static
@@ -522,7 +552,8 @@
       !isolation_info.site_for_cookies().IsNull()) {
     ancestor_chain_bit = blink::mojom::AncestorChainBit::kSameSite;
   }
-  return Create(origin, top_level_site, ancestor_chain_bit);
+  return Create(origin, top_level_site, ancestor_chain_bit,
+                IsThirdPartyStoragePartitioningEnabled());
 }
 
 StorageKey StorageKey::WithOrigin(const url::Origin& origin) const {
@@ -576,15 +607,15 @@
 StorageKey::StorageKey(const url::Origin& origin,
                        const net::SchemefulSite& top_level_site,
                        const base::UnguessableToken* nonce,
-                       blink::mojom::AncestorChainBit ancestor_chain_bit)
+                       blink::mojom::AncestorChainBit ancestor_chain_bit,
+                       bool third_party_partitioning_allowed)
     : origin_(origin),
-      top_level_site_(IsThirdPartyStoragePartitioningEnabled()
+      top_level_site_(third_party_partitioning_allowed
                           ? top_level_site
                           : net::SchemefulSite(origin)),
       top_level_site_if_third_party_enabled_(top_level_site),
       nonce_(base::OptionalFromPtr(nonce)),
-      ancestor_chain_bit_(IsThirdPartyStoragePartitioningEnabled()
-                              ? ancestor_chain_bit
+      ancestor_chain_bit_(third_party_partitioning_allowed ? ancestor_chain_bit
                           : (nonce || origin.opaque())
                               ? blink::mojom::AncestorChainBit::kCrossSite
                               : blink::mojom::AncestorChainBit::kSameSite),
diff --git a/third_party/blink/common/storage_key/storage_key_unittest.cc b/third_party/blink/common/storage_key/storage_key_unittest.cc
index f96f6db..46c843d 100644
--- a/third_party/blink/common/storage_key/storage_key_unittest.cc
+++ b/third_party/blink/common/storage_key/storage_key_unittest.cc
@@ -606,6 +606,11 @@
             false,
         },
         {
+            "https://example.com/^401^50^6",
+            absl::nullopt,
+            false,
+        },
+        {
             "https://example.com/^40^51^6",
             blink::StorageKey::Create(
                 url::Origin::Create(GURL("https://example.com/")),
@@ -614,6 +619,11 @@
             false,
         },
         {
+            "https://example.com/^400^51^6",
+            absl::nullopt,
+            false,
+        },
+        {
             "https://example.com/^41^51^6",
             blink::StorageKey::Create(
                 url::Origin::Create(GURL("https://example.com/")),
@@ -622,6 +632,11 @@
             false,
         },
         {
+            "https://example.com/^41^501^6",
+            absl::nullopt,
+            false,
+        },
+        {
             "https://example.com/^10^20",
             absl::nullopt,
             false,
@@ -634,6 +649,11 @@
             true,
         },
         {
+            "https://example.com/^101^20",
+            absl::nullopt,
+            true,
+        },
+        {
             "https://example.com/^10^21",
             blink::StorageKey::CreateWithNonce(
                 url::Origin::Create(GURL("https://example.com/")),
@@ -641,12 +661,22 @@
             true,
         },
         {
+            "https://example.com/^100^21",
+            absl::nullopt,
+            true,
+        },
+        {
             "https://example.com/^11^21",
             blink::StorageKey::CreateWithNonce(
                 url::Origin::Create(GURL("https://example.com/")),
                 base::UnguessableToken::CreateForTesting(1ULL, 1ULL)),
             true,
         },
+        {
+            "https://example.com/^11^201",
+            absl::nullopt,
+            true,
+        },
     };
 
     for (const auto& test : kTestCases) {
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index b872bc96..f90ce7f9 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -204,6 +204,7 @@
     "interest_group/interest_group.h",
     "interest_group/interest_group_mojom_traits.h",
     "interest_group/interest_group_utils.h",
+    "interest_group/seller_capabilities.h",
     "link_to_text/link_to_text_mojom_traits.h",
     "loader/http_body_element_type.h",
     "loader/inter_process_time_ticks_converter.h",
diff --git a/third_party/blink/public/common/interest_group/interest_group.h b/third_party/blink/public/common/interest_group/interest_group.h
index 0dd6c754..e1363da 100644
--- a/third_party/blink/public/common/interest_group/interest_group.h
+++ b/third_party/blink/public/common/interest_group/interest_group.h
@@ -11,11 +11,11 @@
 #include <string>
 #include <vector>
 
-#include "base/containers/enum_set.h"
 #include "base/containers/flat_map.h"
 #include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/interest_group/seller_capabilities.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom-shared.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -76,17 +76,6 @@
     bool operator==(const Size& other) const;
   };
 
-  enum class SellerCapabilities : uint32_t {
-    kInterestGroupCounts,
-    kLatencyStats,
-
-    kMaxValue = kLatencyStats
-  };
-  using SellerCapabilitiesType =
-      base::EnumSet<SellerCapabilities,
-                    SellerCapabilities::kInterestGroupCounts,
-                    SellerCapabilities::kMaxValue>;
-
   InterestGroup();
 
   // Constructor takes arguments by value. They're unlikely to be independently
@@ -154,7 +143,7 @@
   absl::optional<base::flat_map<std::string, std::vector<std::string>>>
       size_groups;
 
-  static_assert(__LINE__ == 157, R"(
+  static_assert(__LINE__ == 146, R"(
 If modifying InterestGroup fields, make sure to also modify:
 
 * IsValid(), EstimateSize(), and IsEqualForTesting() in this class
diff --git a/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h b/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h
index 4aad93a..1b004c9 100644
--- a/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h
+++ b/third_party/blink/public/common/interest_group/interest_group_mojom_traits.h
@@ -64,21 +64,19 @@
 template <>
 struct BLINK_COMMON_EXPORT
     StructTraits<blink::mojom::SellerCapabilitiesDataView,
-                 blink::InterestGroup::SellerCapabilitiesType> {
+                 blink::SellerCapabilitiesType> {
   static bool allows_interest_group_counts(
-      const blink::InterestGroup::SellerCapabilitiesType& capabilities) {
-    return capabilities.Has(
-        blink::InterestGroup::SellerCapabilities::kInterestGroupCounts);
+      const blink::SellerCapabilitiesType& capabilities) {
+    return capabilities.Has(blink::SellerCapabilities::kInterestGroupCounts);
   }
 
   static bool allows_latency_stats(
-      const blink::InterestGroup::SellerCapabilitiesType& capabilities) {
-    return capabilities.Has(
-        blink::InterestGroup::SellerCapabilities::kLatencyStats);
+      const blink::SellerCapabilitiesType& capabilities) {
+    return capabilities.Has(blink::SellerCapabilities::kLatencyStats);
   }
 
   static bool Read(blink::mojom::SellerCapabilitiesDataView data,
-                   blink::InterestGroup::SellerCapabilitiesType* out);
+                   blink::SellerCapabilitiesType* out);
 };
 
 template <>
@@ -116,13 +114,12 @@
   }
 
   static const absl::optional<
-      base::flat_map<url::Origin,
-                     blink::InterestGroup::SellerCapabilitiesType>>&
+      base::flat_map<url::Origin, blink::SellerCapabilitiesType>>&
   seller_capabilities(const blink::InterestGroup& interest_group) {
     return interest_group.seller_capabilities;
   }
 
-  static blink::InterestGroup::SellerCapabilitiesType all_sellers_capabilities(
+  static blink::SellerCapabilitiesType all_sellers_capabilities(
       const blink::InterestGroup& interest_group) {
     return interest_group.all_sellers_capabilities;
   }
diff --git a/third_party/blink/public/common/interest_group/seller_capabilities.h b/third_party/blink/public/common/interest_group/seller_capabilities.h
new file mode 100644
index 0000000..b677d406
--- /dev/null
+++ b/third_party/blink/public/common/interest_group/seller_capabilities.h
@@ -0,0 +1,26 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_SELLER_CAPABILITIES_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_SELLER_CAPABILITIES_H_
+
+#include "base/containers/enum_set.h"
+
+namespace blink {
+
+enum class SellerCapabilities : uint32_t {
+  kInterestGroupCounts,
+  kLatencyStats,
+
+  kMaxValue = kLatencyStats
+};
+
+using SellerCapabilitiesType =
+    base::EnumSet<SellerCapabilities,
+                  SellerCapabilities::kInterestGroupCounts,
+                  SellerCapabilities::kMaxValue>;
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_SELLER_CAPABILITIES_H_
diff --git a/third_party/blink/public/common/interest_group/test_interest_group_builder.h b/third_party/blink/public/common/interest_group/test_interest_group_builder.h
index 20c100c..2b5b357 100644
--- a/third_party/blink/public/common/interest_group/test_interest_group_builder.h
+++ b/third_party/blink/public/common/interest_group/test_interest_group_builder.h
@@ -42,11 +42,10 @@
       absl::optional<base::flat_map<std::string, double>>
           priority_signals_overrides);
   TestInterestGroupBuilder& SetSellerCapabilities(
-      absl::optional<
-          base::flat_map<url::Origin, InterestGroup::SellerCapabilitiesType>>
+      absl::optional<base::flat_map<url::Origin, SellerCapabilitiesType>>
           seller_capabilities);
   TestInterestGroupBuilder& SetAllSellerCapabilities(
-      InterestGroup::SellerCapabilitiesType all_sellers_capabilities);
+      SellerCapabilitiesType all_sellers_capabilities);
   TestInterestGroupBuilder& SetExecutionMode(
       InterestGroup::ExecutionMode execution_mode);
   TestInterestGroupBuilder& SetBiddingUrl(absl::optional<GURL> bidding_url);
diff --git a/third_party/blink/public/common/storage_key/storage_key.h b/third_party/blink/public/common/storage_key/storage_key.h
index cb0a27f..7c452e6 100644
--- a/third_party/blink/public/common/storage_key/storage_key.h
+++ b/third_party/blink/public/common/storage_key/storage_key.h
@@ -91,9 +91,13 @@
   // (1D) Construct for a specific first or third party context.
   // This is a common entry point when constructing a context, and callsites
   // generally must branch and call CreateWithNonce() if a nonce is set.
+  // TODO(crbug.com/1199077): The default argument here is so tests don't need
+  // to be aware of it. Find a solution that removes this default arg.
   static StorageKey Create(const url::Origin& origin,
                            const net::SchemefulSite& top_level_site,
-                           blink::mojom::AncestorChainBit ancestor_chain_bit);
+                           blink::mojom::AncestorChainBit ancestor_chain_bit,
+                           bool third_party_partitioning_allowed =
+                               IsThirdPartyStoragePartitioningEnabled());
 
   // (1E) Construct for the provided isolation_info.
   // TODO(crbug.com/1346450): This does not account for extension URLs.
@@ -267,7 +271,8 @@
   StorageKey(const url::Origin& origin,
              const net::SchemefulSite& top_level_site,
              const base::UnguessableToken* nonce,
-             blink::mojom::AncestorChainBit ancestor_chain_bit);
+             blink::mojom::AncestorChainBit ancestor_chain_bit,
+             bool third_party_partitioning_allowed);
 
   // (7B) Operators.
   // Note that not all must be friends, but all are to consolidate the header.
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 57f4dcef..bbd52a7 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -13,7 +13,10 @@
 import "services/network/public/mojom/content_security_policy.mojom";
 import "services/network/public/mojom/cross_origin_opener_policy.mojom";
 import "services/network/public/mojom/fetch_api.mojom";
+import "services/network/public/mojom/load_timing_info.mojom";
+import "services/network/public/mojom/network_types.mojom";
 import "services/network/public/mojom/source_location.mojom";
+import "services/network/public/mojom/url_loader_completion_status.mojom";
 import "skia/public/mojom/skcolor.mojom";
 import "third_party/blink/public/mojom/blob/blob.mojom";
 import "third_party/blink/public/mojom/blob/blob_url_store.mojom";
@@ -685,22 +688,6 @@
       => (mojo_base.mojom.String16 content, uint32 start_offset,
           uint32 end_offset);
 
-  // Report ResourceTiming information about cancelled navigation in iframe
-  // initiated from this document. This is required to prevent revealing
-  // information about the status codes to the parent frame. (See crbug.com/1346924)
-  //
-  // Example of cancelled navigations:
-  // - 204 - No Content
-  // - 205 - Reset Content
-  // - Extension blocking navigation (e.g. Ad blocker)
-  // - etc
-  //
-  // |parent_frame_element_type| is used on the blink side to determine the
-  // 'initiator_type' of the entry.
-  AddResourceTimingEntryFromNonNavigatedFrame(
-    ResourceTimingInfo timing,
-    blink.mojom.FrameOwnerElementType parent_frame_element_type);
-
   // Creates an intervention report in the frame with contents |id| and
   // |message|, returns once the report has been queued. |id| identifies the
   // intervention that occurred. |message| is a human-readable string that
@@ -782,13 +769,6 @@
   // network error.
   RenderFallbackContent();
 
-  // Sent to this frame in parent frame's process to render fallback contents.
-  // This is only used for <object> elements that failed a navigation with an
-  // HTTP error.
-  RenderFallbackContentWithResourceTiming(
-      ResourceTimingInfo timing,
-      string server_timing_value);
-
   // Instructs the frame to invoke the beforeunload event handler.
   //
   // The closure callback is invoked to acknowledge the browser that
@@ -983,6 +963,27 @@
   // See https://drafts.csswg.org/css-view-transitions-1/ for details.
   SnapshotDocumentForViewTransition() => (
       blink.mojom.ViewTransitionState view_transition_state);
+
+  // Allows the browser to add a resource timing entry for a subframe
+  // navigation that has failed before committing - a non-ok object navigation
+  // or a no-content response (204/205).
+  AddResourceTimingEntryForFailedSubframeNavigation(
+    blink.mojom.FrameToken subframe_token,
+    url.mojom.Url initial_url,
+    mojo_base.mojom.TimeTicks start_time,
+    mojo_base.mojom.TimeTicks redirect_time,
+    mojo_base.mojom.TimeTicks request_start,
+    mojo_base.mojom.TimeTicks response_start,
+    uint32 response_code,
+    string mime_type,
+    network.mojom.LoadTimingInfo load_timing_info,
+    network.mojom.ConnectionInfo connection_info,
+    string alpn_negotiated_protocol,
+    bool is_secure_transport,
+    bool is_validated,
+    string normalized_server_timing,
+    network.mojom.URLLoaderCompletionStatus completion_status
+  );
 };
 
 // Also implemented in Blink, this interface defines frame-specific methods
diff --git a/third_party/blink/public/mojom/frame/remote_frame.mojom b/third_party/blink/public/mojom/frame/remote_frame.mojom
index c6d96107..dcea8cc 100644
--- a/third_party/blink/public/mojom/frame/remote_frame.mojom
+++ b/third_party/blink/public/mojom/frame/remote_frame.mojom
@@ -120,6 +120,12 @@
   // The navigation initiator's user activation and ad status.
   blink.mojom.NavigationInitiatorActivationAndAdStatus
       initiator_activation_and_ad_status;
+
+  // Whether this navigation was container initiated (e.g. iframe changed src).
+  // Only container-initiated navigations report a resource-timing entry.
+  // Note: when relying on this in the browser process, also make sure to check
+  // that this comes from the renderer process of the parent frame.
+  bool is_container_initiated = false;
 };
 
 // Implemented in Browser, this interface defines frame-specific methods that
@@ -325,13 +331,6 @@
   // network error).
   RenderFallbackContent();
 
-  // Sent to this frame in parent frame's process to render fallback contents.
-  // This is only used for <object> elements that failed a navigation with an
-  // HTTP error.
-  RenderFallbackContentWithResourceTiming(
-      ResourceTimingInfo timing,
-      string server_timing_value);
-
   // Sent to the remote frame placeholder in the parent process so that
   // resource timing information can be added to the parent frame.
   AddResourceTimingFromChild(ResourceTimingInfo timing);
diff --git a/third_party/blink/public/mojom/navigation/navigation_params.mojom b/third_party/blink/public/mojom/navigation/navigation_params.mojom
index f8cf8ce..f81865be 100644
--- a/third_party/blink/public/mojom/navigation/navigation_params.mojom
+++ b/third_party/blink/public/mojom/navigation/navigation_params.mojom
@@ -157,6 +157,16 @@
   // The navigation initiator's user activation and ad status.
   blink.mojom.NavigationInitiatorActivationAndAdStatus
       initiator_activation_and_ad_status;
+
+  // Whether this navigation was container initiated (e.g. iframe changed src).
+  // This is currently partially specified in
+  // https://html.spec.whatwg.org/#create-navigation-params-by-fetching
+  // "If sourceSnapshotParams's fetch client is navigable's container document's
+  // relevant settings object, then set request's initiator type to navigable's
+  // container's local name." This means that only container initiated
+  // navigations should create a resource timing entry.
+  // TODO(https://github.com/whatwg/html/issues/8846): clarify this further.
+  bool is_container_initiated = false;
 };
 
 // Provided by the browser or the renderer -------------------------------------
@@ -272,6 +282,21 @@
 
 // Provided by the browser -----------------------------------------------------
 
+// Only container-initiated navigations report resource timing to the parent.
+// See https://github.com/whatwg/fetch/pull/1579
+enum ParentResourceTimingAccess {
+  kDoNotReport,
+
+  // See https://fetch.spec.whatwg.org/#response-has-cross-origin-redirects
+  // Whether response info (status code, content-type) can be exposed to
+  // resource timing. True for CORS same-origin subresource responses and for
+  // same-origin navigations without cross-origin redirects.
+  // TODO(https://github.com/whatwg/fetch/issues/1602) clarify the spec around
+  // this.
+  kReportWithoutResponseDetails,
+  kReportWithResponseDetails
+};
+
 // Timings collected in the browser during navigation for the
 // Navigation Timing API. Sent to Blink in CommitNavigationParams when
 // the navigation is ready to be committed.
@@ -279,6 +304,8 @@
   mojo_base.mojom.TimeTicks redirect_start;
   mojo_base.mojom.TimeTicks redirect_end;
   mojo_base.mojom.TimeTicks fetch_start;
+
+  ParentResourceTimingAccess parent_resource_timing_access = ParentResourceTimingAccess.kDoNotReport;
 };
 
 // Sent with CommitNavigationParams and should only be set for main-frame
diff --git a/third_party/blink/public/mojom/page/page.mojom b/third_party/blink/public/mojom/page/page.mojom
index 4f543309..d18c661 100644
--- a/third_party/blink/public/mojom/page/page.mojom
+++ b/third_party/blink/public/mojom/page/page.mojom
@@ -74,6 +74,14 @@
   // PerformanceNavigationTiming. It is 0 if this blink::WebViewImpl does not
   // host the main frame, to avoid sending the data cross-origin.
   mojo_base.mojom.TimeTicks activation_start;
+
+  // Holds texture references (stored in the GPU process) and geometry state for
+  // the previous Document displayed by this frame. This is used to support
+  // transitions when navigating between same-origin Documents.
+  // See https://drafts.csswg.org/css-view-transitions-1/ for details.
+  // This is analogous to CommitNavigationParams field but for prerender
+  // activations.
+  blink.mojom.ViewTransitionState? view_transition_state;
 };
 
 // Used for broadcast messages from browser to renderer for messages that need
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index e79be3b5..f6923df 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3818,6 +3818,7 @@
   kFencedFrameConfigAttribute = 4477,
   kURLSearchParams_Has_Delete_MultipleArguments = 4478,
   kPaymentHandlerMinimalHeaderUX = 4479,
+  kPopoverTypeHint = 4480,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 4dfaca0..87b97a7 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -191,6 +191,10 @@
 
   // The initiator frame's LocalDOMWindow's has_storage_access state.
   bool has_storage_access = false;
+  // Whether this navigation was initiated by the container, e.g. iframe changed
+  // src. Only container-initiated navigation report resource timing to the
+  // parent.
+  bool is_container_initiated = false;
 };
 
 // This structure holds all information provided by the embedder that is
@@ -534,6 +538,10 @@
 
   // Whether the document should be loaded with the has_storage_access bit set.
   bool has_storage_access = false;
+  // Whether this navigation should report resource timing to the parent,
+  // and if so, whether it should expose/hide response details.
+  mojom::ParentResourceTimingAccess parent_resource_timing_access =
+      mojom::ParentResourceTimingAccess::kDoNotReport;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_navigation_timings.h b/third_party/blink/public/web/web_navigation_timings.h
index 6f0a079..dc713ca 100644
--- a/third_party/blink/public/web/web_navigation_timings.h
+++ b/third_party/blink/public/web/web_navigation_timings.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_NAVIGATION_TIMINGS_H_
 
 #include "base/time/time.h"
+#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-shared.h"
 
 namespace blink {
 
@@ -15,6 +16,7 @@
   base::TimeTicks redirect_start;
   base::TimeTicks redirect_end;
   base::TimeTicks fetch_start;
+  blink::mojom::ParentResourceTimingAccess parent_resource_timing_access;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl
index 80a9a1a2..7e2ca3b 100644
--- a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl
@@ -8,6 +8,7 @@
 #include "base/containers/flat_map.h"
 #include "third_party/blink/public/mojom/runtime_feature_state/runtime_feature_state.mojom-shared.h"
 #include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_read_context.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
 
 namespace blink {
 
@@ -16,11 +17,16 @@
   initial_values_.reserve({{browser_read_access_features|length()}});
 
   {% for feature in browser_read_access_features %}
-  // TODO(crbug.com/1377000): fetch current value.
   initial_values_.insert(
       {blink::mojom::RuntimeFeatureState::k{{feature.name}},
         false});
   {% endfor %}
+
+  // TODO(crbug.com/1377000): Specifying each starting value this way is rigid.
+  // Find a more flexible way to do so.
+  initial_values_
+      [blink::mojom::RuntimeFeatureState::kThirdPartyStoragePartitioning] =
+          StorageKey::IsThirdPartyStoragePartitioningEnabled();
 }
 
 }  // namespace blink
\ No newline at end of file
diff --git a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl
index a147d63..5f0b69b 100644
--- a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl
@@ -35,7 +35,7 @@
   }
 
   {% for feature in browser_read_access_features %}
-  bool Is{{feature.name}}Enabled() {
+  bool Is{{feature.name}}Enabled() const {
     return IsEnabled(
         blink::mojom::RuntimeFeatureState::k{{feature.name}});
   }
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 586ef27..c8ea315e 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -76,6 +76,7 @@
     "+services/network/public/cpp/request_destination.h",
     "+services/network/public/cpp/request_mode.h",
     "+services/network/public/cpp/resource_request.h",
+    "+services/network/public/cpp/url_loader_completion_status.h",
     "+services/network/public/cpp/web_sandbox_flags.h",
     "+services/service_manager/public",
     "+skia/public/mojom",
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index d368e53d..bdfb88e 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -147,6 +147,8 @@
   "css_identifier_value.h",
   "css_image_generator_value.cc",
   "css_image_generator_value.h",
+  "css_image_set_option_value.cc",
+  "css_image_set_option_value.h",
   "css_image_set_value.cc",
   "css_image_set_value.h",
   "css_image_value.cc",
diff --git a/third_party/blink/renderer/core/css/css_image_set_option_value.cc b/third_party/blink/renderer/core/css/css_image_set_option_value.cc
new file mode 100644
index 0000000..4e5d509a
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_image_set_option_value.cc
@@ -0,0 +1,123 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/css_image_set_option_value.h"
+
+#include "third_party/blink/renderer/core/css/css_gradient_value.h"
+#include "third_party/blink/renderer/core/css/css_image_value.h"
+#include "third_party/blink/renderer/core/style/style_generated_image.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+namespace {
+
+const CSSValue* ComputeImage(const CSSValue* value,
+                             const ComputedStyle& style,
+                             const bool allow_visited_style) {
+  if (auto* image = DynamicTo<CSSImageValue>(value)) {
+    return image->ComputedCSSValue();
+  }
+
+  if (!RuntimeEnabledFeatures::CSSImageSetEnabled()) {
+    return value;
+  }
+
+  if (auto* gradient = DynamicTo<cssvalue::CSSGradientValue>(value)) {
+    return gradient->ComputedCSSValue(style, allow_visited_style);
+  }
+
+  NOTREACHED();
+
+  return value;
+}
+
+const CSSNumericLiteralValue* ComputeResolution(
+    const CSSNumericLiteralValue* resolution) {
+  if (RuntimeEnabledFeatures::CSSImageSetEnabled() && resolution &&
+      resolution->IsResolution() &&
+      resolution->GetType() != CSSPrimitiveValue::UnitType::kDotsPerPixel) {
+    return CSSNumericLiteralValue::Create(
+        resolution->ComputeDotsPerPixel(),
+        CSSPrimitiveValue::UnitType::kDotsPerPixel);
+  }
+
+  return resolution;
+}
+
+}  // namespace
+
+CSSImageSetOptionValue::CSSImageSetOptionValue(
+    const CSSValue* image,
+    const CSSNumericLiteralValue* resolution)
+    : CSSValue(kImageSetOptionClass), image_(image), resolution_(resolution) {
+  DCHECK(image);
+
+  if (!resolution_) {
+    resolution_ =
+        CSSNumericLiteralValue::Create(1.0, CSSPrimitiveValue::UnitType::kX);
+  }
+}
+
+CSSImageSetOptionValue::~CSSImageSetOptionValue() = default;
+
+StyleImage* CSSImageSetOptionValue::CacheImage(
+    const Document& document,
+    const FetchParameters::ImageRequestBehavior image_request_behavior,
+    const CrossOriginAttributeValue cross_origin,
+    const CSSToLengthConversionData::ContainerSizes& container_sizes) const {
+  if (auto* image =
+          const_cast<CSSImageValue*>(DynamicTo<CSSImageValue>(image_.Get()))) {
+    return image->CacheImage(document, image_request_behavior, cross_origin,
+                             ComputedResolution());
+  }
+
+  if (!RuntimeEnabledFeatures::CSSImageSetEnabled()) {
+    return nullptr;
+  }
+
+  if (auto* gradient = DynamicTo<cssvalue::CSSGradientValue>(image_.Get())) {
+    return MakeGarbageCollected<StyleGeneratedImage>(*gradient,
+                                                     container_sizes);
+  }
+
+  NOTREACHED();
+
+  return nullptr;
+}
+
+double CSSImageSetOptionValue::ComputedResolution() const {
+  return resolution_->ComputeDotsPerPixel();
+}
+
+String CSSImageSetOptionValue::CustomCSSText() const {
+  StringBuilder result;
+
+  result.Append(image_->CssText());
+  result.Append(' ');
+  result.Append(resolution_->CssText());
+
+  return result.ReleaseString();
+}
+
+bool CSSImageSetOptionValue::Equals(const CSSImageSetOptionValue& other) const {
+  return *image_ == *other.image_ && *resolution_ == *other.resolution_;
+}
+
+CSSImageSetOptionValue* CSSImageSetOptionValue::ComputedCSSValue(
+    const ComputedStyle& style,
+    const bool allow_visited_style) const {
+  return MakeGarbageCollected<CSSImageSetOptionValue>(
+      ComputeImage(image_, style, allow_visited_style),
+      ComputeResolution(resolution_));
+}
+
+void CSSImageSetOptionValue::TraceAfterDispatch(blink::Visitor* visitor) const {
+  visitor->Trace(image_);
+  visitor->Trace(resolution_);
+
+  CSSValue::TraceAfterDispatch(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_image_set_option_value.h b/third_party/blink/renderer/core/css/css_image_set_option_value.h
new file mode 100644
index 0000000..b650b75
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_image_set_option_value.h
@@ -0,0 +1,67 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_IMAGE_SET_OPTION_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_IMAGE_SET_OPTION_VALUE_H_
+
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
+#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
+#include "third_party/blink/renderer/core/css/css_value.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/style/style_image.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// This class represents an image-set-option as specified in:
+// https://w3c.github.io/csswg-drafts/css-images-4/#typedef-image-set-option
+// <image-set-option> = [ <image> | <string> ] [<resolution> || type(<string>)]
+class CSSImageSetOptionValue : public CSSValue {
+ public:
+  explicit CSSImageSetOptionValue(
+      const CSSValue* image,
+      const CSSNumericLiteralValue* resolution = nullptr);
+
+  // It is expected that CSSImageSetOptionValue objects should always have
+  // non-null image and resolution values.
+  CSSImageSetOptionValue() = delete;
+
+  ~CSSImageSetOptionValue();
+
+  StyleImage* CacheImage(
+      const Document& document,
+      const FetchParameters::ImageRequestBehavior image_request_behavior,
+      const CrossOriginAttributeValue cross_origin,
+      const CSSToLengthConversionData::ContainerSizes& container_sizes) const;
+
+  // Gets the resolution value in Dots Per Pixel
+  double ComputedResolution() const;
+
+  String CustomCSSText() const;
+
+  bool Equals(const CSSImageSetOptionValue& other) const;
+
+  CSSImageSetOptionValue* ComputedCSSValue(
+      const ComputedStyle& style,
+      const bool allow_visited_style) const;
+
+  void TraceAfterDispatch(blink::Visitor* visitor) const;
+
+ private:
+  Member<const CSSValue> image_;
+  Member<const CSSNumericLiteralValue> resolution_;
+};
+
+template <>
+struct DowncastTraits<CSSImageSetOptionValue> {
+  static bool AllowFrom(const CSSValue& value) {
+    return value.IsImageSetOptionValue();
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_IMAGE_SET_OPTION_VALUE_H_
diff --git a/third_party/blink/renderer/core/css/css_image_set_value.cc b/third_party/blink/renderer/core/css/css_image_set_value.cc
index 8439b55..72f2532b 100644
--- a/third_party/blink/renderer/core/css/css_image_set_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_set_value.cc
@@ -27,22 +27,8 @@
 
 #include <algorithm>
 
-#include "third_party/blink/public/common/loader/referrer_utils.h"
-#include "third_party/blink/renderer/core/css/css_gradient_value.h"
-#include "third_party/blink/renderer/core/css/css_image_value.h"
-#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
-#include "third_party/blink/renderer/core/css/css_primitive_value.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
-#include "third_party/blink/renderer/core/style/style_generated_image.h"
 #include "third_party/blink/renderer/core/style/style_image_set.h"
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
-#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/weborigin/referrer.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -52,7 +38,7 @@
 
 CSSImageSetValue::~CSSImageSetValue() = default;
 
-const CSSImageSetValue::ImageSetOption& CSSImageSetValue::GetBestOption(
+const CSSImageSetOptionValue* CSSImageSetValue::GetBestOption(
     const float device_scale_factor) {
   // This method is implementing the selection logic described in the
   // "CSS Images Module Level 4" spec:
@@ -75,26 +61,21 @@
   //      <image-set-option>."
 
   if (options_.empty()) {
-    for (wtf_size_t i = 0, length = this->length(); i < length; ++i) {
-      auto image_index = i;
-
-      ++i;
-      SECURITY_DCHECK(i < length);
-      float resolution = To<CSSPrimitiveValue>(Item(i)).ComputeDotsPerPixel();
-
-      options_.push_back(ImageSetOption{image_index, resolution});
+    for (const auto& i : *this) {
+      options_.push_back(To<CSSImageSetOptionValue>(i.Get()));
     }
 
-    std::stable_sort(
-        options_.begin(), options_.end(),
-        [](const ImageSetOption& left, const ImageSetOption& right) {
-          return left.resolution < right.resolution;
-        });
+    std::stable_sort(options_.begin(), options_.end(),
+                     [](const CSSImageSetOptionValue* left,
+                        const CSSImageSetOptionValue* right) {
+                       return left->ComputedResolution() <
+                              right->ComputedResolution();
+                     });
   }
 
-  for (const auto& image : options_) {
-    if (image.resolution >= device_scale_factor) {
-      return image;
+  for (const auto& option : options_) {
+    if (option->ComputedResolution() >= device_scale_factor) {
+      return option;
     }
   }
 
@@ -121,9 +102,11 @@
     const CrossOriginAttributeValue cross_origin,
     const CSSToLengthConversionData::ContainerSizes& container_sizes) {
   if (IsCachePending(device_scale_factor)) {
-    StyleImage* style_image =
-        GetImageToCache(device_scale_factor, document, image_request_behavior,
-                        cross_origin, container_sizes);
+    const CSSImageSetOptionValue* best_option =
+        GetBestOption(device_scale_factor);
+
+    StyleImage* style_image = best_option->CacheImage(
+        document, image_request_behavior, cross_origin, container_sizes);
 
     cached_image_ = MakeGarbageCollected<StyleImageSet>(style_image, this);
 
@@ -133,34 +116,6 @@
   return cached_image_.Get();
 }
 
-StyleImage* CSSImageSetValue::GetImageToCache(
-    const float device_scale_factor,
-    const Document& document,
-    const FetchParameters::ImageRequestBehavior image_request_behavior,
-    const CrossOriginAttributeValue cross_origin,
-    const CSSToLengthConversionData::ContainerSizes& container_sizes) {
-  const ImageSetOption& best_option = GetBestOption(device_scale_factor);
-
-  const CSSValue& image_value = Item(best_option.index);
-
-  if (auto* image =
-          const_cast<CSSImageValue*>(DynamicTo<CSSImageValue>(image_value))) {
-    return image->CacheImage(document, image_request_behavior, cross_origin,
-                             best_option.resolution);
-  }
-
-  if (!RuntimeEnabledFeatures::CSSImageSetEnabled()) {
-    return nullptr;
-  }
-
-  if (auto* gradient = DynamicTo<cssvalue::CSSGradientValue>(image_value)) {
-    return MakeGarbageCollected<StyleGeneratedImage>(*gradient,
-                                                     container_sizes);
-  }
-
-  return nullptr;
-}
-
 String CSSImageSetValue::CustomCSSText() const {
   StringBuilder result;
 
@@ -175,17 +130,11 @@
       result.Append(", ");
     }
 
-    const CSSValue& image_value = Item(i);
-    result.Append(image_value.CssText());
-    result.Append(' ');
-
-    ++i;
-    SECURITY_DCHECK(i < length);
-    const CSSValue& resolution_value = Item(i);
-    result.Append(resolution_value.CssText());
+    result.Append(Item(i).CssText());
   }
 
   result.Append(')');
+
   return result.ReleaseString();
 }
 
@@ -193,14 +142,17 @@
   if (!cached_image_) {
     return false;
   }
+
   if (ImageResourceContent* cached_content = cached_image_->CachedImage()) {
     return cached_content->LoadFailedOrCanceled();
   }
+
   return true;
 }
 
 void CSSImageSetValue::TraceAfterDispatch(blink::Visitor* visitor) const {
   visitor->Trace(cached_image_);
+  visitor->Trace(options_);
   CSSValueList::TraceAfterDispatch(visitor);
 }
 
@@ -209,34 +161,9 @@
     const bool allow_visited_style) const {
   auto* value = MakeGarbageCollected<CSSImageSetValue>();
 
-  for (auto& item : *this) {
-    value->Append(
-        *ComputedCSSValueForOption(item.Get(), style, allow_visited_style));
-  }
-
-  return value;
-}
-
-const CSSValue* CSSImageSetValue::ComputedCSSValueForOption(
-    const CSSValue* value,
-    const ComputedStyle& style,
-    const bool allow_visited_style) const {
-  if (auto* image = DynamicTo<CSSImageValue>(value)) {
-    return image->ComputedCSSValue();
-  }
-
-  if (RuntimeEnabledFeatures::CSSImageSetEnabled()) {
-    if (auto* resolution = DynamicTo<CSSNumericLiteralValue>(value);
-        resolution && resolution->IsResolution() &&
-        resolution->GetType() != CSSPrimitiveValue::UnitType::kDotsPerPixel) {
-      return CSSNumericLiteralValue::Create(
-          resolution->ComputeDotsPerPixel(),
-          CSSPrimitiveValue::UnitType::kDotsPerPixel);
-    }
-
-    if (auto* gradient = DynamicTo<cssvalue::CSSGradientValue>(value)) {
-      return gradient->ComputedCSSValue(style, allow_visited_style);
-    }
+  for (const auto& i : *this) {
+    value->Append(*To<CSSImageSetOptionValue>(i.Get())->ComputedCSSValue(
+        style, allow_visited_style));
   }
 
   return value;
diff --git a/third_party/blink/renderer/core/css/css_image_set_value.h b/third_party/blink/renderer/core/css/css_image_set_value.h
index 54787e9..172865bb 100644
--- a/third_party/blink/renderer/core/css/css_image_set_value.h
+++ b/third_party/blink/renderer/core/css/css_image_set_value.h
@@ -26,8 +26,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_IMAGE_SET_VALUE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_IMAGE_SET_VALUE_H_
 
+#include "third_party/blink/renderer/core/css/css_image_set_option_value.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
+#include "third_party/blink/renderer/core/style/style_image.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -63,30 +66,12 @@
   void TraceAfterDispatch(blink::Visitor*) const;
 
  private:
-  struct ImageSetOption {
-    wtf_size_t index{};
-    float resolution{};
-  };
-
-  const ImageSetOption& GetBestOption(const float device_scale_factor);
-
-  StyleImage* GetImageToCache(
-      const float device_scale_factor,
-      const Document& document,
-      const FetchParameters::ImageRequestBehavior image_request_behavior,
-      const CrossOriginAttributeValue cross_origin,
-      const CSSToLengthConversionData::ContainerSizes& container_sizes);
-
-  // Gets the computed CSS value of image-set-option components.
-  const CSSValue* ComputedCSSValueForOption(
-      const CSSValue* value,
-      const ComputedStyle& style,
-      const bool allow_visited_style) const;
+  const CSSImageSetOptionValue* GetBestOption(const float device_scale_factor);
 
   Member<StyleImage> cached_image_;
   float cached_device_scale_factor_{1.0f};
 
-  Vector<ImageSetOption> options_;
+  HeapVector<Member<const CSSImageSetOptionValue>> options_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/css/css_value.cc b/third_party/blink/renderer/core/css/css_value.cc
index 4ea8d905..a86e8d6 100644
--- a/third_party/blink/renderer/core/css/css_value.cc
+++ b/third_party/blink/renderer/core/css/css_value.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h"
 #include "third_party/blink/renderer/core/css/css_grid_template_areas_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_image_set_option_value.h"
 #include "third_party/blink/renderer/core/css/css_image_set_value.h"
 #include "third_party/blink/renderer/core/css/css_image_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
@@ -290,6 +291,8 @@
         return CompareCSSValues<CSSValueList>(*this, other);
       case kValuePairClass:
         return CompareCSSValues<CSSValuePair>(*this, other);
+      case kImageSetOptionClass:
+        return CompareCSSValues<CSSImageSetOptionValue>(*this, other);
       case kImageSetClass:
         return CompareCSSValues<CSSImageSetValue>(*this, other);
       case kCSSContentDistributionClass:
@@ -435,6 +438,8 @@
       return To<CSSValuePair>(this)->CustomCSSText();
     case kValueListClass:
       return To<CSSValueList>(this)->CustomCSSText();
+    case kImageSetOptionClass:
+      return To<CSSImageSetOptionValue>(this)->CustomCSSText();
     case kImageSetClass:
       return To<CSSImageSetValue>(this)->CustomCSSText();
     case kCSSContentDistributionClass:
@@ -653,6 +658,9 @@
     case kValuePairClass:
       To<CSSValuePair>(this)->TraceAfterDispatch(visitor);
       return;
+    case kImageSetOptionClass:
+      To<CSSImageSetOptionValue>(this)->TraceAfterDispatch(visitor);
+      return;
     case kImageSetClass:
       To<CSSImageSetValue>(this)->TraceAfterDispatch(visitor);
       return;
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h
index 0beb748..4deb0b0 100644
--- a/third_party/blink/renderer/core/css/css_value.h
+++ b/third_party/blink/renderer/core/css/css_value.h
@@ -103,6 +103,9 @@
     return class_type_ >= kLinearGradientClass &&
            class_type_ <= kConicGradientClass;
   }
+  bool IsImageSetOptionValue() const {
+    return class_type_ == kImageSetOptionClass;
+  }
   bool IsImageSetValue() const { return class_type_ == kImageSetClass; }
   bool IsImageValue() const { return class_type_ == kImageClass; }
   bool IsInheritedValue() const { return class_type_ == kInheritedClass; }
@@ -289,6 +292,8 @@
     kKeyframeShorthandClass,
     kInitialColorValueClass,
 
+    kImageSetOptionClass,
+
     // List class types must appear after ValueListClass.
     kValueListClass,
     kFunctionClass,
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 90787c0..e582f9a 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h"
 #include "third_party/blink/renderer/core/css/css_grid_template_areas_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_image_set_option_value.h"
 #include "third_party/blink/renderer/core/css/css_image_set_value.h"
 #include "third_party/blink/renderer/core/css/css_image_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
@@ -1463,7 +1464,7 @@
   return nullptr;
 }
 
-CSSPrimitiveValue* ConsumeResolution(CSSParserTokenRange& range) {
+CSSNumericLiteralValue* ConsumeResolution(CSSParserTokenRange& range) {
   const CSSParserToken& token = range.Peek();
 
   // Unlike the other types, calc() does not work with <resolution>.
@@ -3491,45 +3492,46 @@
         ConsumeGeneratedImagePolicy::kAllow) {
   CSSParserTokenRange range_copy = range;
   CSSParserTokenRange args = ConsumeFunction(range_copy);
+
   auto* image_set = MakeGarbageCollected<CSSImageSetValue>();
+
   do {
+    CSSValue* image = nullptr;
+
     AtomicString url_value =
         (RuntimeEnabledFeatures::CSSImageSetEnabled()
              ? ConsumeUrlOrStringAsStringView(args, context)
              : ConsumeUrlAsStringView(args, context))
             .ToAtomicString();
     if (!url_value.IsNull()) {
-      image_set->Append(*CreateCSSImageValueWithReferrer(url_value, context));
+      image = CreateCSSImageValueWithReferrer(url_value, context);
     } else {
       if (!RuntimeEnabledFeatures::CSSImageSetEnabled()) {
         return nullptr;
       }
 
-      CSSValue* gen_image = ConsumeGeneratedImage(args, context);
-      if (gen_image == nullptr) {
+      image = ConsumeGeneratedImage(args, context);
+      if (image == nullptr) {
         return nullptr;
       }
-
-      image_set->Append(*gen_image);
     }
 
-    if (args.Peek().GetType() != kDimensionToken &&
-        RuntimeEnabledFeatures::CSSImageSetEnabled()) {
-      image_set->Append(*CSSNumericLiteralValue::Create(
-          1.0, CSSPrimitiveValue::UnitType::kX));
-    } else {
+    CSSNumericLiteralValue* resolution = nullptr;
+    if (args.Peek().GetType() == kDimensionToken ||
+        !RuntimeEnabledFeatures::CSSImageSetEnabled()) {
       if (args.Peek().GetUnitType() != CSSPrimitiveValue::UnitType::kX &&
           !RuntimeEnabledFeatures::CSSImageSetEnabled()) {
         return nullptr;
       }
 
-      const CSSPrimitiveValue* resolution = ConsumeResolution(args);
+      resolution = ConsumeResolution(args);
       if (resolution == nullptr || resolution->GetDoubleValue() <= 0.0) {
         return nullptr;
       }
-
-      image_set->Append(*resolution);
     }
+
+    image_set->Append(
+        *MakeGarbageCollected<CSSImageSetOptionValue>(image, resolution));
   } while (ConsumeCommaIncludingWhitespace(args));
 
   if (!args.AtEnd()) {
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index 30ab4c82..65bb101 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -142,7 +142,7 @@
 CSSPrimitiveValue* ConsumeTime(CSSParserTokenRange&,
                                const CSSParserContext&,
                                CSSPrimitiveValue::ValueRange);
-CSSPrimitiveValue* ConsumeResolution(CSSParserTokenRange&);
+CSSNumericLiteralValue* ConsumeResolution(CSSParserTokenRange&);
 CSSValue* ConsumeRatio(CSSParserTokenRange&, const CSSParserContext&);
 CSSIdentifierValue* ConsumeIdent(CSSParserTokenRange&);
 CSSIdentifierValue* ConsumeIdentRange(CSSParserTokenRange&,
diff --git a/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc b/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc
index 0b42f6f..d0bfc91 100644
--- a/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc
@@ -60,8 +60,7 @@
   void Add(const TestKey& key,
            const ComputedStyle& style,
            const ComputedStyle& parent_style) {
-    cache_.Add(key.InnerKey(), ComputedStyle::Clone(style),
-               ComputedStyle::Clone(parent_style));
+    cache_.Add(key.InnerKey(), &style, &parent_style);
   }
 
   const CachedMatchedProperties* Find(const TestKey& key,
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 0eb13b8..a690190 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -2117,7 +2117,7 @@
                                   matched_property_cache_added, 1);
     matched_properties_cache_.Add(cache_success.key,
                                   state.StyleBuilder().CloneStyle(),
-                                  ComputedStyle::Clone(*state.ParentStyle()));
+                                  state.ParentStyle());
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 49933db..c387c06 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3314,7 +3314,8 @@
     //
     // This update is also necessary if the first body element changes because
     // another body element is inserted or removed.
-    layout_object->SetStyle(ComputedStyle::Clone(*layout_object->Style()));
+    layout_object->SetStyle(
+        ComputedStyleBuilder(*layout_object->Style()).TakeStyle());
   }
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 9c29a03..269e291 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7919,7 +7919,17 @@
   return nullptr;
 }
 
-HTMLElement* Document::TopmostPopover() const {
+void Document::SetPopoverHintShowing(HTMLElement* element) {
+  DCHECK(!element || element->HasPopoverAttribute());
+  DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+  popover_hint_showing_ = element;
+}
+
+HTMLElement* Document::TopmostPopoverOrHint() const {
+  if (PopoverHintShowing()) {
+    DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+    return PopoverHintShowing();
+  }
   if (PopoverStack().empty())
     return nullptr;
   return PopoverStack().back();
@@ -8677,6 +8687,7 @@
   visitor->Trace(top_layer_elements_);
   visitor->Trace(top_layer_elements_pending_removal_);
   visitor->Trace(popover_stack_);
+  visitor->Trace(popover_hint_showing_);
   visitor->Trace(popover_pointerdown_target_);
   visitor->Trace(popovers_waiting_to_hide_);
   visitor->Trace(elements_with_css_toggles_);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index f8316015..0b5ec66 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1545,12 +1545,14 @@
 
   HTMLDialogElement* ActiveModalDialog() const;
 
+  HTMLElement* PopoverHintShowing() const { return popover_hint_showing_; }
+  void SetPopoverHintShowing(HTMLElement* element);
   HeapVector<Member<HTMLElement>>& PopoverStack() { return popover_stack_; }
   const HeapVector<Member<HTMLElement>>& PopoverStack() const {
     return popover_stack_;
   }
   bool PopoverAutoShowing() const { return !popover_stack_.empty(); }
-  HTMLElement* TopmostPopover() const;
+  HTMLElement* TopmostPopoverOrHint() const;
   HeapHashSet<Member<HTMLElement>>& PopoversWaitingToHide() {
     return popovers_waiting_to_hide_;
   }
@@ -2425,6 +2427,8 @@
   // The stack of currently-displayed `popover=auto` elements. Elements in the
   // stack go from earliest (bottom-most) to latest (top-most).
   HeapVector<Member<HTMLElement>> popover_stack_;
+  // The `popover=hint` that is currently showing, if any.
+  Member<HTMLElement> popover_hint_showing_;
   // The popover (if any) that received the most recent pointerdown event.
   Member<const HTMLElement> popover_pointerdown_target_;
   // A set of popovers for which hidePopover() has been called, but animations
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 3ed58a8..12f001d 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -3369,24 +3369,41 @@
   // prerenderchange event and post-prerendering activation steps on each
   // document, which could mutate the frame tree and make iteration over it
   // complicated.
-  HeapVector<Member<Document>> documents;
+  HeapVector<Member<Document>> child_frame_documents;
+  Member<Document> main_frame_document;
+  if (auto* local_frame = DynamicTo<LocalFrame>(GetPage()->MainFrame())) {
+    main_frame_document = local_frame->GetDocument();
+  }
+
   for (Frame* frame = GetPage()->MainFrame(); frame;
        frame = frame->Tree().TraverseNext()) {
-    if (auto* local_frame = DynamicTo<LocalFrame>(frame))
-      documents.push_back(local_frame->GetDocument());
+    if (auto* local_frame = DynamicTo<LocalFrame>(frame)) {
+      if (local_frame->GetDocument() != main_frame_document) {
+        child_frame_documents.push_back(local_frame->GetDocument());
+      }
+    }
   }
 
   // A null `activation_start` is sent to the WebViewImpl that does not host the
   // main frame, in which case we expect that it does not have any documents
   // since cross-origin documents are not loaded during prerendering.
-  DCHECK(documents.size() == 0 ||
+  DCHECK((!main_frame_document && child_frame_documents.size() == 0) ||
          !prerender_page_activation_params->activation_start.is_null());
+  // We also only send view_transition_state to the main frame.
+  DCHECK(main_frame_document ||
+         !prerender_page_activation_params->view_transition_state);
+
+  if (main_frame_document) {
+    main_frame_document->ActivateForPrerendering(
+        *prerender_page_activation_params);
+    prerender_page_activation_params->view_transition_state.reset();
+  }
 
   // While the spec says to post a task on the networking task source for each
   // document, we don't post a task here for simplicity. This allows dispatching
   // the event on all documents without a chance for other IPCs from the browser
   // to arrive in the intervening time, resulting in an unclear state.
-  for (auto& document : documents) {
+  for (auto& document : child_frame_documents) {
     document->ActivateForPrerendering(*prerender_page_activation_params);
   }
 
diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc
index b211523..22bf66c 100644
--- a/third_party/blink/renderer/core/frame/frame.cc
+++ b/third_party/blink/renderer/core/frame/frame.cc
@@ -342,17 +342,6 @@
       HTMLObjectElement::ErrorEventPolicy::kDispatch);
 }
 
-void Frame::RenderFallbackContentWithResourceTiming(
-    mojom::blink::ResourceTimingInfoPtr timing,
-    const String& server_timing_value) {
-  auto* local_dom_window = To<LocalDOMWindow>(Parent()->DomWindow());
-  DOMWindowPerformance::performance(*local_dom_window)
-      ->AddResourceTimingWithUnparsedServerTiming(
-          std::move(timing), server_timing_value,
-          html_names::kObjectTag.LocalName());
-  RenderFallbackContent();
-}
-
 bool Frame::IsInFencedFrameTree() const {
   DCHECK(!IsDetached());
   if (!features::IsFencedFramesEnabled())
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index df85fa1..5cfb8cd 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -481,9 +481,6 @@
   void ClearUserActivationInFrameTree();
 
   void RenderFallbackContent();
-  void RenderFallbackContentWithResourceTiming(
-      mojom::blink::ResourceTimingInfoPtr timing,
-      const String& server_timing_values);
 
   // Only implemented for LocalFrames.
   virtual void ActivateHistoryUserActivationState() {}
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index d3a43f5..e26eebb 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -229,28 +229,6 @@
 
 namespace {
 
-const AtomicString& ConvertElementTypeToInitiatorType(
-    blink::FrameOwnerElementType frame_owner_elem_type) {
-  switch (frame_owner_elem_type) {
-    case blink::FrameOwnerElementType::kFrame:
-      return blink::html_names::kFrameTag.LocalName();
-    case blink::FrameOwnerElementType::kIframe:
-      return blink::html_names::kIFrameTag.LocalName();
-    case blink::FrameOwnerElementType::kObject:
-      return blink::html_names::kObjectTag.LocalName();
-    case blink::FrameOwnerElementType::kFencedframe:
-      return blink::html_names::kFencedframeTag.LocalName();
-    case blink::FrameOwnerElementType::kEmbed:
-      return blink::html_names::kEmbedTag.LocalName();
-    case blink::FrameOwnerElementType::kPortal:
-      return blink::html_names::kPortalTag.LocalName();
-    case blink::FrameOwnerElementType::kNone:
-      NOTREACHED();
-  }
-  NOTREACHED();
-  return blink::html_names::kFrameTag.LocalName();
-}
-
 // Maintain a global (statically-allocated) hash map indexed by the the result
 // of hashing the |frame_token| passed on creation of a LocalFrame object.
 using LocalFramesByTokenMap = HeapHashMap<uint64_t, WeakMember<LocalFrame>>;
@@ -749,15 +727,6 @@
   return local_root.clip_path_paint_image_generator_.Get();
 }
 
-void LocalFrame::AddResourceTimingEntryFromNonNavigatedFrame(
-    mojom::blink::ResourceTimingInfoPtr timing,
-    blink::FrameOwnerElementType initiator_type) {
-  auto* local_dom_window = DomWindow();
-  DOMWindowPerformance::performance(*local_dom_window)
-      ->AddResourceTiming(std::move(timing),
-                          ConvertElementTypeToInitiatorType(initiator_type));
-}
-
 const SecurityContext* LocalFrame::GetSecurityContext() const {
   return DomWindow() ? &DomWindow()->GetSecurityContext() : nullptr;
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 4d4a5aa..bd10cad9 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -293,10 +293,6 @@
   BoxShadowPaintImageGenerator* GetBoxShadowPaintImageGenerator();
   ClipPathPaintImageGenerator* GetClipPathPaintImageGenerator();
 
-  void AddResourceTimingEntryFromNonNavigatedFrame(
-      mojom::blink::ResourceTimingInfoPtr timing,
-      blink::FrameOwnerElementType initiator_type);
-
   // A local root is the root of a connected subtree that contains only
   // LocalFrames. The local root is responsible for coordinating input, layout,
   // et cetera for that subtree of frames.
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 553afb5..2e876bb 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -187,7 +187,8 @@
       const LocalFrameToken* initiator_frame_token,
       std::unique_ptr<SourceLocation> source_location,
       mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>
-          initiator_policy_container_handle) = 0;
+          initiator_policy_container_handle,
+      bool is_container_initiated) = 0;
 
   virtual void DispatchWillSendSubmitEvent(HTMLFormElement*) = 0;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
index c4a59332..3e43e9d7 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
@@ -526,7 +526,8 @@
     const LocalFrameToken* initiator_frame_token,
     std::unique_ptr<SourceLocation> source_location,
     mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>
-        initiator_policy_container_keep_alive_handle) {
+        initiator_policy_container_keep_alive_handle,
+    bool is_container_initiated) {
   if (!web_frame_->Client())
     return;
 
@@ -685,6 +686,7 @@
                                                     .GetSandboxFlags();
 
   navigation_info->href_translate = href_translate;
+  navigation_info->is_container_initiated = is_container_initiated;
 
   web_frame_->Client()->BeginNavigation(std::move(navigation_info));
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.h b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
index 5d2c642..fc56200 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
@@ -138,7 +138,8 @@
       const LocalFrameToken* initiator_frame_token,
       std::unique_ptr<SourceLocation> source_location,
       mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>
-          initiator_policy_container_keep_alive_handle) override;
+          initiator_policy_container_keep_alive_handle,
+      bool is_container_initiated) override;
   void DispatchWillSendSubmitEvent(HTMLFormElement*) override;
   void DidStartLoading() override;
   void DidStopLoading() override;
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index 238edb9c5..fe43461 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -10,6 +10,8 @@
 #include "build/build_config.h"
 #include "components/power_scheduler/power_mode.h"
 #include "components/power_scheduler/power_mode_arbiter.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/chrome_debug_urls.h"
@@ -67,6 +69,7 @@
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
 #include "third_party/blink/renderer/core/view_transition/view_transition_supplement.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h"
 #include "third_party/blink/renderer/platform/widget/frame_widget.h"
 
 #if BUILDFLAG(IS_MAC)
@@ -667,20 +670,6 @@
   frame_->RenderFallbackContent();
 }
 
-void LocalFrameMojoHandler::RenderFallbackContentWithResourceTiming(
-    mojom::blink::ResourceTimingInfoPtr timing,
-    const String& server_timing_value) {
-  frame_->RenderFallbackContentWithResourceTiming(std::move(timing),
-                                                  server_timing_value);
-}
-
-void LocalFrameMojoHandler::AddResourceTimingEntryFromNonNavigatedFrame(
-    mojom::blink::ResourceTimingInfoPtr timing,
-    blink::FrameOwnerElementType parent_frame_owner_element_type) {
-  frame_->AddResourceTimingEntryFromNonNavigatedFrame(
-      std::move(timing), parent_frame_owner_element_type);
-}
-
 void LocalFrameMojoHandler::BeforeUnload(bool is_reload,
                                          BeforeUnloadCallback callback) {
   base::TimeTicks before_unload_start_time = base::TimeTicks::Now();
@@ -1356,6 +1345,51 @@
   BeforeUnload(is_reload, std::move(callback));
 }
 
+void LocalFrameMojoHandler::AddResourceTimingEntryForFailedSubframeNavigation(
+    const FrameToken& subframe_token,
+    const KURL& initial_url,
+    base::TimeTicks start_time,
+    base::TimeTicks redirect_time,
+    base::TimeTicks request_start,
+    base::TimeTicks response_start,
+    uint32_t response_code,
+    const WTF::String& mime_type,
+    network::mojom::blink::LoadTimingInfoPtr load_timing_info,
+    net::HttpResponseInfo::ConnectionInfo connection_info,
+    const WTF::String& alpn_negotiated_protocol,
+    bool is_secure_transport,
+    bool is_validated,
+    const WTF::String& normalized_server_timing,
+    const network::URLLoaderCompletionStatus& completion_status) {
+  Frame* subframe = Frame::ResolveFrame(subframe_token);
+  if (!subframe || !subframe->Owner()) {
+    return;
+  }
+
+  ResourceResponse response;
+  response.SetAlpnNegotiatedProtocol(AtomicString(alpn_negotiated_protocol));
+  response.SetConnectionInfo(connection_info);
+  response.SetConnectionReused(load_timing_info->socket_reused);
+  response.SetTimingAllowPassed(true);
+  response.SetIsValidated(is_validated);
+  response.SetDecodedBodyLength(completion_status.decoded_body_length);
+  response.SetEncodedBodyLength(completion_status.encoded_body_length);
+  response.SetEncodedDataLength(completion_status.encoded_data_length);
+  response.SetHttpStatusCode(response_code);
+  if (!normalized_server_timing.empty()) {
+    response.SetHttpHeaderField("Server-Timing",
+                                AtomicString(normalized_server_timing));
+  }
+
+  mojom::blink::ResourceTimingInfoPtr info =
+      CreateResourceTimingInfo(start_time, initial_url, &response);
+  info->response_end = completion_status.completion_time;
+  info->last_redirect_end_time = redirect_time;
+  info->is_secure_transport = is_secure_transport;
+  info->timing = std::move(load_timing_info);
+  subframe->Owner()->AddResourceTiming(std::move(info));
+}
+
 void LocalFrameMojoHandler::RequestFullscreenVideoElement() {
   // Find the first video element of the frame.
   for (auto* child = frame_->GetDocument()->documentElement(); child;
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
index 9e4185a5..a31557c9 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "third_party/blink/public/mojom/input/text_input_host.mojom-blink.h"
@@ -118,12 +119,6 @@
   void SaveImageAt(const gfx::Point& window_point) final;
   void ReportBlinkFeatureUsage(const Vector<mojom::blink::WebFeature>&) final;
   void RenderFallbackContent() final;
-  void RenderFallbackContentWithResourceTiming(
-      mojom::blink::ResourceTimingInfoPtr timing,
-      const String& server_timing_values) final;
-  void AddResourceTimingEntryFromNonNavigatedFrame(
-      mojom::blink::ResourceTimingInfoPtr timing,
-      blink::FrameOwnerElementType parent_frame_owner_element_type) final;
   void BeforeUnload(bool is_reload, BeforeUnloadCallback callback) final;
   void MediaPlayerActionAt(
       const gfx::Point& window_point,
@@ -207,6 +202,23 @@
   void SnapshotDocumentForViewTransition(
       SnapshotDocumentForViewTransitionCallback callback) final;
 
+  void AddResourceTimingEntryForFailedSubframeNavigation(
+      const FrameToken& subframe_token,
+      const KURL& initial_url,
+      base::TimeTicks start_time,
+      base::TimeTicks redirect_time,
+      base::TimeTicks request_start,
+      base::TimeTicks response_start,
+      uint32_t response_code,
+      const WTF::String& mime_type,
+      network::mojom::blink::LoadTimingInfoPtr load_timing_info,
+      net::HttpResponseInfo::ConnectionInfo connection_info,
+      const WTF::String& alpn_negotiated_protocol,
+      bool is_secure_transport,
+      bool is_validated,
+      const WTF::String& normalized_server_timing,
+      const ::network::URLLoaderCompletionStatus& completion_status) final;
+
   // blink::mojom::LocalMainFrame overrides:
   void AnimateDoubleTapZoom(const gfx::Point& point,
                             const gfx::Rect& rect) override;
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index a5c5ed7..b8546a7a 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -304,6 +304,7 @@
       GetNavigationInitiatorActivationAndAdStatus(request.HasUserGesture(),
                                                   is_ad_script_in_stack);
 
+  params->is_container_initiated = frame_request.IsContainerInitiated();
   GetRemoteFrameHostRemote().OpenURL(std::move(params));
 }
 
@@ -409,25 +410,11 @@
   Frame::RenderFallbackContent();
 }
 
-void RemoteFrame::RenderFallbackContentWithResourceTiming(
-    mojom::blink::ResourceTimingInfoPtr timing,
-    const String& server_timing_value) {
-  Frame::RenderFallbackContentWithResourceTiming(std::move(timing),
-                                                 server_timing_value);
-}
-
 void RemoteFrame::AddResourceTimingFromChild(
     mojom::blink::ResourceTimingInfoPtr timing) {
   HTMLFrameOwnerElement* owner_element = To<HTMLFrameOwnerElement>(Owner());
   DCHECK(owner_element);
-
-  if (!owner_element->HasPendingFallbackTimingInfo()) {
-    return;
-  }
-
-  DOMWindowPerformance::performance(*owner_element->GetDocument().domWindow())
-      ->AddResourceTiming(std::move(timing), owner_element->localName());
-  owner_element->DidReportResourceTiming();
+  owner_element->AddResourceTiming(std::move(timing));
 }
 
 void RemoteFrame::DidStartLoading() {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index 6c1aaec..eedd7f6 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -182,9 +182,6 @@
       const base::UnguessableToken& embedding_token) override;
   void SetPageFocus(bool is_focused) override;
   void RenderFallbackContent() override;
-  void RenderFallbackContentWithResourceTiming(
-      mojom::blink::ResourceTimingInfoPtr timing,
-      const String& server_timing_values) final;
   void ScrollRectToVisible(
       const gfx::RectF& rect_to_scroll,
       mojom::blink::ScrollIntoViewParamsPtr params) override;
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 862d9ac3..8c639c1 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -210,7 +210,8 @@
           document.GetExecutionContext())) {
     HTMLElement::HideAllPopoversUntil(
         nullptr, document, HidePopoverFocusBehavior::kNone,
-        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions);
+        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+        HidePopoverIndependence::kHideUnrelated);
   }
 
   // To fullscreen an |element| within a |document|, set the |element|'s
diff --git a/third_party/blink/renderer/core/html/forms/base_button_input_type.cc b/third_party/blink/renderer/core/html/forms/base_button_input_type.cc
index 417394a16..5933067e 100644
--- a/third_party/blink/renderer/core/html/forms/base_button_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/base_button_input_type.cc
@@ -65,7 +65,7 @@
 }
 
 void BaseButtonInputType::ValueAttributeChanged() {
-  To<Text>(GetElement().UserAgentShadowRoot()->firstChild())
+  To<Text>(GetElement().EnsureShadowSubtree()->firstChild())
       ->setData(GetElement().ValueOrDefaultLabel());
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/chooser_only_temporal_input_type_view.cc b/third_party/blink/renderer/core/html/forms/chooser_only_temporal_input_type_view.cc
index 8dbfdeb..d75b73da 100644
--- a/third_party/blink/renderer/core/html/forms/chooser_only_temporal_input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/chooser_only_temporal_input_type_view.cc
@@ -102,7 +102,7 @@
 }
 
 void ChooserOnlyTemporalInputTypeView::UpdateView() {
-  Node* node = GetElement().UserAgentShadowRoot()->firstChild();
+  Node* node = GetElement().EnsureShadowSubtree()->firstChild();
   auto* html_element = DynamicTo<HTMLElement>(node);
   if (!html_element)
     return;
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index e698265..a20cd839 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -363,14 +363,14 @@
 }
 
 HTMLInputElement* FileInputType::UploadButton() const {
-  Element* element = GetElement().UserAgentShadowRoot()->getElementById(
+  Element* element = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdFileUploadButton);
   CHECK(!element || IsA<HTMLInputElement>(element));
   return To<HTMLInputElement>(element);
 }
 
 Node* FileInputType::FileStatusElement() const {
-  return GetElement().UserAgentShadowRoot()->lastChild();
+  return GetElement().EnsureShadowSubtree()->lastChild();
 }
 
 void FileInputType::DisabledAttributeChanged() {
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type_test.cc b/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
index 5e2ad55..981372a 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
@@ -243,7 +243,7 @@
   // Receiving a FileChooser response should not alter a shadow tree
   // for another type.
   EXPECT_TRUE(IsA<HTMLElement>(
-      input.UserAgentShadowRoot()->firstChild()->firstChild()));
+      input.EnsureShadowSubtree()->firstChild()->firstChild()));
 }
 
 // Tests selecting same file twice should fire cancel event second time.
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index 3fa23d3..06f6d61 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
+#include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
 #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h"
 #include "third_party/blink/renderer/core/dom/id_target_observer.h"
@@ -148,12 +149,6 @@
                       : MakeGarbageCollected<TextInputType>(*this)),
       input_type_view_(input_type_ ? input_type_->CreateView() : nullptr) {
   SetHasCustomStyleCallbacks();
-
-  if (!flags.IsCreatedByParser()) {
-    DCHECK(input_type_view_->NeedsShadowSubtree());
-    CreateUserAgentShadowRoot();
-    CreateShadowSubtree();
-  }
 }
 
 void HTMLInputElement::Trace(Visitor* visitor) const {
@@ -383,6 +378,7 @@
 }
 
 void HTMLInputElement::setType(const AtomicString& type) {
+  EnsureShadowSubtree();
   setAttribute(html_names::kTypeAttr, type);
 }
 
@@ -400,11 +396,6 @@
     non_attribute_value_ = SanitizeValue(default_value);
   has_been_password_field_ |= new_type_name == input_type_names::kPassword;
 
-  if (input_type_view_->NeedsShadowSubtree()) {
-    CreateUserAgentShadowRoot();
-    CreateShadowSubtree();
-  }
-
   UpdateWillValidateCache();
 
   if (!default_value.IsNull())
@@ -477,10 +468,8 @@
   input_type_view_->WillBeDestroyed();
   input_type_ = new_type;
   input_type_view_ = input_type_->CreateView();
-  if (input_type_view_->NeedsShadowSubtree()) {
-    EnsureUserAgentShadowRoot();
-    CreateShadowSubtree();
-  }
+
+  input_type_view_->CreateShadowSubtreeIfNeeded();
 
   UpdateWillValidateCache();
 
@@ -1512,8 +1501,9 @@
     TextControlElement::DefaultEventHandler(evt);
 }
 
-void HTMLInputElement::CreateShadowSubtree() {
-  input_type_view_->CreateShadowSubtree();
+ShadowRoot* HTMLInputElement::EnsureShadowSubtree() {
+  input_type_view_->CreateShadowSubtreeIfNeeded();
+  return UserAgentShadowRoot();
 }
 
 bool HTMLInputElement::HasActivationBehavior() const {
@@ -1720,6 +1710,10 @@
   ResetListAttributeTargetObserver();
   LogAddElementIfIsolatedWorldAndInDocument("input", html_names::kTypeAttr,
                                             html_names::kFormactionAttr);
+  {
+    EventDispatchForbiddenScope::AllowUserAgentEvents allow_events;
+    input_type_view_->CreateShadowSubtreeIfNeeded();
+  }
   return kInsertionShouldCallDidNotifySubtreeInsertions;
 }
 
@@ -1966,6 +1960,11 @@
   return input_type_->SupportsPlaceholder();
 }
 
+TextControlInnerEditorElement* HTMLInputElement::EnsureInnerEditorElement()
+    const {
+  return input_type_view_->EnsureInnerEditorElement();
+}
+
 void HTMLInputElement::UpdatePlaceholderText() {
   return input_type_view_->UpdatePlaceholderText(!SuggestedValue().empty());
 }
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.h b/third_party/blink/renderer/core/html/forms/html_input_element.h
index 513daa0..fa8a8b76 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.h
@@ -370,9 +370,10 @@
   bool isMutable();
   void showPicker(ExceptionState&);
 
+  ShadowRoot* EnsureShadowSubtree();
+
  protected:
   void DefaultEventHandler(Event&) override;
-  void CreateShadowSubtree();
 
  private:
   enum AutoCompleteSetting { kUninitialized, kOn, kOff };
@@ -439,6 +440,7 @@
   bool TooLong(const String&, NeedsToCheckDirtyFlag) const;
   bool TooShort(const String&, NeedsToCheckDirtyFlag) const;
 
+  TextControlInnerEditorElement* EnsureInnerEditorElement() const final;
   void UpdatePlaceholderText() final;
   bool IsEmptyValue() const final { return InnerEditorValue().empty(); }
   void HandleBlurEvent() final;
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element_test.cc b/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
index 6fa1dd6..7aa8d08 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
@@ -150,7 +150,7 @@
 TEST_F(HTMLInputElementTest, create) {
   auto* input = MakeGarbageCollected<HTMLInputElement>(
       GetDocument(), CreateElementFlags::ByCreateElement());
-  EXPECT_NE(nullptr, input->UserAgentShadowRoot());
+  EXPECT_EQ(nullptr, input->UserAgentShadowRoot());
 
   input = MakeGarbageCollected<HTMLInputElement>(
       GetDocument(), CreateElementFlags::ByParser(&GetDocument()));
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
index f87fd80e..e2e8c75 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
@@ -682,6 +682,11 @@
   is_placeholder_visible_ = visible;
 }
 
+TextControlInnerEditorElement* HTMLTextAreaElement::EnsureInnerEditorElement()
+    const {
+  return InnerEditorElement();
+}
+
 void HTMLTextAreaElement::UpdatePlaceholderText() {
   HTMLElement* placeholder = PlaceholderElement();
   const String placeholder_text = GetPlaceholderValue();
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.h b/third_party/blink/renderer/core/html/forms/html_text_area_element.h
index 69813f0..6b06d31 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.h
@@ -97,6 +97,7 @@
   String GetPlaceholderValue() const final;
   void UpdatePlaceholderText() override;
   bool IsEmptyValue() const override { return Value().empty(); }
+  TextControlInnerEditorElement* EnsureInnerEditorElement() const final;
 
   bool IsOptionalFormControl() const override {
     return !IsRequiredFormControl();
diff --git a/third_party/blink/renderer/core/html/forms/input_type_view.cc b/third_party/blink/renderer/core/html/forms/input_type_view.cc
index ea94f0c4..c5560ce6 100644
--- a/third_party/blink/renderer/core/html/forms/input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/input_type_view.cc
@@ -130,8 +130,22 @@
   return true;
 }
 
+TextControlInnerEditorElement* InputTypeView::EnsureInnerEditorElement() {
+  CreateShadowSubtreeIfNeeded();
+  return GetElement().InnerEditorElement();
+}
+
 void InputTypeView::CreateShadowSubtree() {}
 
+void InputTypeView::CreateShadowSubtreeIfNeeded() {
+  if (has_created_shadow_subtree_ || !NeedsShadowSubtree()) {
+    return;
+  }
+  GetElement().EnsureUserAgentShadowRoot();
+  has_created_shadow_subtree_ = true;
+  CreateShadowSubtree();
+}
+
 void InputTypeView::DestroyShadowSubtree() {
   if (ShadowRoot* root = GetElement().UserAgentShadowRoot())
     root->RemoveChildren();
diff --git a/third_party/blink/renderer/core/html/forms/input_type_view.h b/third_party/blink/renderer/core/html/forms/input_type_view.h
index bff1971..f8d8d57 100644
--- a/third_party/blink/renderer/core/html/forms/input_type_view.h
+++ b/third_party/blink/renderer/core/html/forms/input_type_view.h
@@ -59,6 +59,7 @@
 class LayoutObject;
 enum class LegacyLayout;
 class MouseEvent;
+class TextControlInnerEditorElement;
 
 class ClickHandlingState final : public EventDispatchHandlingState {
  public:
@@ -119,6 +120,9 @@
 
   // Functions for shadow trees
 
+  TextControlInnerEditorElement* EnsureInnerEditorElement();
+  bool HasCreatedShadowSubtree() const { return has_created_shadow_subtree_; }
+  void CreateShadowSubtreeIfNeeded();
   virtual bool NeedsShadowSubtree() const;
   virtual void CreateShadowSubtree();
   virtual void DestroyShadowSubtree();
@@ -161,6 +165,7 @@
   bool will_be_destroyed_ = false;
 
  private:
+  bool has_created_shadow_subtree_ = false;
   Member<HTMLInputElement> element_;
 };
 
diff --git a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
index b7b9ef34..ecbfc9eb 100644
--- a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
@@ -142,7 +142,7 @@
 
 DateTimeEditElement*
 MultipleFieldsTemporalInputTypeView::GetDateTimeEditElement() const {
-  auto* element = GetElement().UserAgentShadowRoot()->getElementById(
+  auto* element = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdDateTimeEdit);
   CHECK(!element || IsA<DateTimeEditElement>(element));
   return To<DateTimeEditElement>(element);
@@ -150,7 +150,7 @@
 
 SpinButtonElement* MultipleFieldsTemporalInputTypeView::GetSpinButtonElement()
     const {
-  auto* element = GetElement().UserAgentShadowRoot()->getElementById(
+  auto* element = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdSpinButton);
   CHECK(!element || IsA<SpinButtonElement>(element));
   return To<SpinButtonElement>(element);
@@ -158,7 +158,7 @@
 
 ClearButtonElement* MultipleFieldsTemporalInputTypeView::GetClearButtonElement()
     const {
-  auto* element = GetElement().UserAgentShadowRoot()->getElementById(
+  auto* element = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdClearButton);
   CHECK(!element || IsA<ClearButtonElement>(element));
   return To<ClearButtonElement>(element);
@@ -166,7 +166,7 @@
 
 PickerIndicatorElement*
 MultipleFieldsTemporalInputTypeView::GetPickerIndicatorElement() const {
-  auto* element = GetElement().UserAgentShadowRoot()->getElementById(
+  auto* element = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdPickerIndicator);
   CHECK(!element || IsA<PickerIndicatorElement>(element));
   return To<PickerIndicatorElement>(element);
@@ -174,7 +174,7 @@
 
 inline bool MultipleFieldsTemporalInputTypeView::ContainsFocusedShadowElement()
     const {
-  return GetElement().UserAgentShadowRoot()->contains(
+  return GetElement().EnsureShadowSubtree()->contains(
       GetElement().GetDocument().FocusedElement());
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/password_input_type.cc b/third_party/blink/renderer/core/html/forms/password_input_type.cc
index 394a0da..a5a3c6dc 100644
--- a/third_party/blink/renderer/core/html/forms/password_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/password_input_type.cc
@@ -152,7 +152,7 @@
 }
 
 void PasswordInputType::UpdatePasswordRevealButton() {
-  Element* button = GetElement().UserAgentShadowRoot()->getElementById(
+  Element* button = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdPasswordRevealButton);
 
   // Update the glyph.
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc
index 0b197552..2cd86c9 100644
--- a/third_party/blink/renderer/core/html/forms/range_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -152,6 +152,10 @@
 }
 
 void RangeInputType::HandleMouseDownEvent(MouseEvent& event) {
+  if (!HasCreatedShadowSubtree()) {
+    return;
+  }
+
   if (GetElement().IsDisabledFormControl())
     return;
 
@@ -299,7 +303,9 @@
 }
 
 void RangeInputType::UpdateView() {
-  GetSliderThumbElement()->SetPositionFromValue();
+  if (HasCreatedShadowSubtree()) {
+    GetSliderThumbElement()->SetPositionFromValue();
+  }
 }
 
 String RangeInputType::SanitizeValue(const String& proposed_value) const {
@@ -317,6 +323,9 @@
 }
 
 void RangeInputType::DisabledAttributeChanged() {
+  if (!HasCreatedShadowSubtree()) {
+    return;
+  }
   if (GetElement().IsDisabledFormControl())
     GetSliderThumbElement()->StopDragging();
 }
@@ -332,6 +341,10 @@
 }
 
 inline Element* RangeInputType::SliderTrackElement() const {
+  if (!HasCreatedShadowSubtree()) {
+    return nullptr;
+  }
+
   return GetElement().UserAgentShadowRoot()->getElementById(
       shadow_element_names::kIdSliderTrack);
 }
@@ -341,7 +354,7 @@
   if (auto* object = GetElement().GetLayoutObject())
     object->SetSubtreeShouldDoFullPaintInvalidation();
   Element* slider_track_element = SliderTrackElement();
-  if (slider_track_element->GetLayoutObject()) {
+  if (slider_track_element && slider_track_element->GetLayoutObject()) {
     slider_track_element->GetLayoutObject()->SetNeedsLayout(
         layout_invalidation_reason::kAttributeChanged);
   }
diff --git a/third_party/blink/renderer/core/html/forms/search_input_type.cc b/third_party/blink/renderer/core/html/forms/search_input_type.cc
index 34ab3adf..4e3e822 100644
--- a/third_party/blink/renderer/core/html/forms/search_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/search_input_type.cc
@@ -147,7 +147,7 @@
 }
 
 void SearchInputType::UpdateCancelButtonVisibility() {
-  Element* button = GetElement().UserAgentShadowRoot()->getElementById(
+  Element* button = GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdSearchClearButton);
   if (!button)
     return;
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
index 9c7f28d..265ed91 100644
--- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
+++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -95,7 +95,7 @@
 
 void SliderThumbElement::SetPositionFromPoint(const LayoutPoint& point) {
   HTMLInputElement* input(HostInput());
-  Element* track_element = input->UserAgentShadowRoot()->getElementById(
+  Element* track_element = input->EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdSliderTrack);
 
   const LayoutObject* input_object = input->GetLayoutObject();
@@ -329,8 +329,10 @@
 
 void SliderContainerElement::HandleTouchEvent(TouchEvent* event) {
   HTMLInputElement* input = HostInput();
-  if (!input || input->IsDisabledFormControl() || !event)
+  if (!input || !input->UserAgentShadowRoot() ||
+      input->IsDisabledFormControl() || !event) {
     return;
+  }
 
   if (event->type() == event_type_names::kTouchend) {
     // TODO: Also do this for touchcancel?
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.cc b/third_party/blink/renderer/core/html/forms/text_control_element.cc
index 6c92c005..e9e6dcd 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.cc
@@ -157,7 +157,9 @@
   if (event.type() == event_type_names::kBlur ||
       event.type() == event_type_names::kFocus)
     return;
-  InnerEditorElement()->DefaultEventHandler(event);
+  if (auto* inner_editor = InnerEditorElement()) {
+    inner_editor->DefaultEventHandler(event);
+  }
 }
 
 String TextControlElement::StrippedPlaceholder() const {
@@ -197,11 +199,13 @@
 }
 
 HTMLElement* TextControlElement::PlaceholderElement() const {
+  ShadowRoot* root = UserAgentShadowRoot();
+  if (!root) {
+    return nullptr;
+  }
   if (!SupportsPlaceholder())
     return nullptr;
-  DCHECK(UserAgentShadowRoot());
-  auto* element = UserAgentShadowRoot()->getElementById(
-      shadow_element_names::kIdPlaceholder);
+  auto* element = root->getElementById(shadow_element_names::kIdPlaceholder);
   CHECK(!element || IsA<HTMLElement>(element));
   return To<HTMLElement>(element);
 }
@@ -481,7 +485,7 @@
   if (ShouldApplySelectionCache() || !isConnected())
     return did_change;
 
-  HTMLElement* inner_editor = InnerEditorElement();
+  HTMLElement* inner_editor = EnsureInnerEditorElement();
   if (!frame || !inner_editor)
     return did_change;
 
@@ -848,10 +852,8 @@
   if (!IsTextControl() || OpenShadowRoot())
     return;
 
-  DCHECK(InnerEditorElement());
-
   bool text_is_changed = value != InnerEditorValue();
-  HTMLElement* inner_editor = InnerEditorElement();
+  HTMLElement* inner_editor = EnsureInnerEditorElement();
   if (!text_is_changed && inner_editor->HasChildren())
     return;
 
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.h b/third_party/blink/renderer/core/html/forms/text_control_element.h
index 8ac4c3f..2d6dc7cb 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.h
@@ -136,6 +136,7 @@
   TextControlInnerEditorElement* InnerEditorElement() const {
     return inner_editor_;
   }
+  virtual TextControlInnerEditorElement* EnsureInnerEditorElement() const = 0;
   HTMLElement* CreateInnerEditorElement();
   void DropInnerEditorElement() { inner_editor_ = nullptr; }
 
diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
index 81c74c1..d5e522b 100644
--- a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
@@ -127,6 +127,9 @@
 }
 
 SpinButtonElement* TextFieldInputType::GetSpinButtonElement() const {
+  if (!HasCreatedShadowSubtree()) {
+    return nullptr;
+  }
   auto* element = GetElement().UserAgentShadowRoot()->getElementById(
       shadow_element_names::kIdSpinButton);
   CHECK(!element || IsA<SpinButtonElement>(element));
@@ -353,7 +356,7 @@
 }
 
 Element* TextFieldInputType::ContainerElement() const {
-  return GetElement().UserAgentShadowRoot()->getElementById(
+  return GetElement().EnsureShadowSubtree()->getElementById(
       shadow_element_names::kIdTextFieldContainer);
 }
 
@@ -364,6 +367,9 @@
 }
 
 void TextFieldInputType::ListAttributeTargetChanged() {
+  if (!HasCreatedShadowSubtree()) {
+    return;
+  }
   if (ChromeClient* chrome_client = GetChromeClient())
     chrome_client->TextFieldDataListChanged(GetElement());
   Element* picker = GetElement().UserAgentShadowRoot()->getElementById(
@@ -417,10 +423,16 @@
 }
 
 void TextFieldInputType::DisabledAttributeChanged() {
+  if (!HasCreatedShadowSubtree()) {
+    return;
+  }
   DisabledOrReadonlyAttributeChanged();
 }
 
 void TextFieldInputType::ReadonlyAttributeChanged() {
+  if (!HasCreatedShadowSubtree()) {
+    return;
+  }
   DisabledOrReadonlyAttributeChanged();
 }
 
@@ -519,6 +531,9 @@
 }
 
 void TextFieldInputType::UpdatePlaceholderText(bool is_suggested_value) {
+  if (!HasCreatedShadowSubtree()) {
+    return;
+  }
   if (!SupportsPlaceholder())
     return;
   HTMLElement* placeholder = GetElement().PlaceholderElement();
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.cc b/third_party/blink/renderer/core/html/html_dialog_element.cc
index f6b03a2..e1c14129 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.cc
+++ b/third_party/blink/renderer/core/html/html_dialog_element.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -61,7 +62,8 @@
           document.GetExecutionContext())) {
     HTMLElement::HideAllPopoversUntil(
         nullptr, document, HidePopoverFocusBehavior::kNone,
-        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions);
+        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+        HidePopoverIndependence::kHideUnrelated);
   }
 
   dialog->previously_focused_element_ = document.FocusedElement();
@@ -357,7 +359,8 @@
           GetDocument().GetExecutionContext())) {
     HTMLElement::HideAllPopoversUntil(
         nullptr, GetDocument(), HidePopoverFocusBehavior::kNone,
-        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions);
+        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+        HidePopoverIndependence::kHideUnrelated);
   }
 
   Element* control = GetFocusDelegate(/*autofocus_only=*/false);
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 5745a1e..241080981 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -100,6 +100,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
 #include "third_party/blink/renderer/platform/text/bidi_resolver.h"
 #include "third_party/blink/renderer/platform/text/bidi_text_run.h"
@@ -1161,6 +1162,9 @@
   if (EqualIgnoringASCIICase(value, kPopoverTypeValueAuto) ||
       (!value.IsNull() && value.empty())) {
     return PopoverValueType::kAuto;
+  } else if (EqualIgnoringASCIICase(value, kPopoverTypeValueHint) &&
+             RuntimeEnabledFeatures::HTMLPopoverHintEnabled()) {
+    return PopoverValueType::kHint;
   } else if (EqualIgnoringASCIICase(value, kPopoverTypeValueManual)) {
     return PopoverValueType::kManual;
   } else if (!value.IsNull()) {
@@ -1238,6 +1242,9 @@
     case PopoverValueType::kAuto:
       UseCounter::Count(GetDocument(), WebFeature::kPopoverTypeAuto);
       break;
+    case PopoverValueType::kHint:
+      UseCounter::Count(GetDocument(), WebFeature::kPopoverTypeHint);
+      break;
     case PopoverValueType::kManual:
       UseCounter::Count(GetDocument(), WebFeature::kPopoverTypeManual);
       break;
@@ -1254,6 +1261,27 @@
   return GetPopoverData();
 }
 
+AtomicString HTMLElement::popover() const {
+  auto attribute_value =
+      FastGetAttribute(html_names::kPopoverAttr).LowerASCII();
+  if (attribute_value.IsNull()) {
+    return attribute_value;  // Nullable
+  } else if (attribute_value.empty()) {
+    return kPopoverTypeValueAuto;  // ReflectEmpty = "auto"
+  } else if (attribute_value == kPopoverTypeValueAuto ||
+             attribute_value == kPopoverTypeValueManual) {
+    return attribute_value;  // ReflectOnly
+  } else if (attribute_value == kPopoverTypeValueHint &&
+             RuntimeEnabledFeatures::HTMLPopoverHintEnabled()) {
+    return attribute_value;  // ReflectOnly (with HTMLPopoverHint enabled)
+  } else {
+    return kPopoverTypeValueManual;  // ReflectInvalid = "manual"
+  }
+}
+void HTMLElement::setPopover(const AtomicString& value) {
+  setAttribute(html_names::kPopoverAttr, value);
+}
+
 PopoverValueType HTMLElement::PopoverType() const {
   return GetPopoverData() ? GetPopoverData()->type() : PopoverValueType::kNone;
 }
@@ -1398,13 +1426,33 @@
   bool should_restore_focus = false;
   auto& document = GetDocument();
   auto original_type = PopoverType();
-  if (original_type == PopoverValueType::kAuto) {
-    // If the new popover is a popover=auto, hide any popover above this in the
-    // stack, if any.
-    const auto* auto_ancestor = FindTopmostPopoverAncestor(*this);
-    HideAllPopoversUntil(
-        auto_ancestor, document, HidePopoverFocusBehavior::kNone,
-        HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions);
+  if (original_type == PopoverValueType::kAuto ||
+      original_type == PopoverValueType::kHint) {
+    const auto* ancestor = FindTopmostPopoverAncestor(*this);
+    if (original_type == PopoverValueType::kHint) {
+      DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+      // If the new popover is popover=hint, hide other hints first.
+      if (document.PopoverHintShowing()) {
+        document.PopoverHintShowing()->HidePopoverInternal(
+            HidePopoverFocusBehavior::kNone,
+            HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+            exception_state);
+      }
+      // Then hide open popovers that aren't ancestors of this hint.
+      if (ancestor) {
+        HideAllPopoversUntil(
+            ancestor, document, HidePopoverFocusBehavior::kNone,
+            HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+            HidePopoverIndependence::kHideUnrelated);
+      }
+    } else {
+      // If the new popover is a popover=auto, hide any popover above this in
+      // the stack, if any.
+      HideAllPopoversUntil(
+          ancestor, document, HidePopoverFocusBehavior::kNone,
+          HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+          HidePopoverIndependence::kHideUnrelated);
+    }
 
     // The 'beforetoggle' event handlers could have changed this popover, e.g.
     // by changing its type, removing it from the document, or calling
@@ -1423,13 +1471,19 @@
       return;
     }
 
-    // We only restore focus for popover=auto, and only for the first popover in
-    // the stack. If there's nothing showing, restore focus.
-    should_restore_focus = !document.TopmostPopover();
+    // We only restore focus for popover=auto/hint, and only for the first
+    // popover in the stack. If there's nothing showing, restore focus.
+    should_restore_focus = !document.TopmostPopoverOrHint();
+
     // Add this popover to the popover stack.
-    auto& stack = document.PopoverStack();
-    DCHECK(!stack.Contains(this));
-    stack.push_back(this);
+    if (original_type == PopoverValueType::kAuto) {
+      auto& stack = document.PopoverStack();
+      DCHECK(!stack.Contains(this));
+      stack.push_back(this);
+    } else {
+      DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+      document.SetPopoverHintShowing(this);
+    }
   }
 
   GetPopoverData()->setPreviouslyFocusedElement(nullptr);
@@ -1485,51 +1539,98 @@
 }
 
 // static
-// All popovers up to, but not including, |endpoint|, will be hidden.
+// All popovers up to, but not including, |endpoint|, will be hidden. If there
+// are "unrelated" popovers open, such as a stack of popover=auto popovers and
+// |endpoint| is a popover=hint, then the popover_independence argument controls
+// whether those unrelated popover=auto popovers are hidden.
 void HTMLElement::HideAllPopoversUntil(
     const HTMLElement* endpoint,
     Document& document,
     HidePopoverFocusBehavior focus_behavior,
-    HidePopoverTransitionBehavior transition_behavior) {
+    HidePopoverTransitionBehavior transition_behavior,
+    HidePopoverIndependence popover_independence) {
   DCHECK(RuntimeEnabledFeatures::HTMLPopoverAttributeEnabled(
       document.GetExecutionContext()));
   DCHECK(!endpoint || endpoint->HasPopoverAttribute());
-
+  DCHECK(endpoint ||
+         popover_independence == HidePopoverIndependence::kHideUnrelated);
+  // We never throw exceptions from HideAllPopoversUntil, since it is always
+  // used to close other popovers that are already showing.
+  ExceptionState* exception_state = nullptr;
   auto close_all_open_popovers = [&document, &focus_behavior,
-                                  &transition_behavior]() {
-    while (auto* popover = document.TopmostPopover()) {
-      // We never throw exceptions from HideAllPopoversUntil, since it is always
-      // used to close other popovers that are already showing.
+                                  &transition_behavior, &exception_state]() {
+    while (auto* popover = document.TopmostPopoverOrHint()) {
       popover->HidePopoverInternal(focus_behavior, transition_behavior,
-                                   /*exception_state=*/nullptr);
+                                   exception_state);
     }
   };
 
   if (!endpoint)
     return close_all_open_popovers();
 
-  DCHECK_EQ(endpoint->PopoverType(), PopoverValueType::kAuto);
-  // Then hide everything in the popover=auto stack until the last_to_hide
-  // popover is closed, or the stack is empty.
-  const HTMLElement* last_to_hide = nullptr;
-  bool found_endpoint = false;
-  for (auto popover : document.PopoverStack()) {
-    if (popover == endpoint) {
-      found_endpoint = true;
-    } else if (found_endpoint) {
-      last_to_hide = popover;
-      break;
+  if (endpoint->PopoverType() == PopoverValueType::kHint) {
+    DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+    if (popover_independence == HidePopoverIndependence::kHideUnrelated) {
+      if (document.PopoverHintShowing() &&
+          document.PopoverHintShowing() != endpoint) {
+        document.PopoverHintShowing()->HidePopoverInternal(
+            focus_behavior, transition_behavior, exception_state);
+      }
+      // Close all auto popovers.
+      for (auto popover : document.PopoverStack()) {
+        popover->HidePopoverInternal(focus_behavior, transition_behavior,
+                                     exception_state);
+      }
     }
-  }
-  if (!found_endpoint)
-    return close_all_open_popovers();
-  while (last_to_hide && last_to_hide->popoverOpen() &&
-         !document.PopoverStack().empty()) {
-    // We never throw exceptions from HideAllPopoversUntil, since it is always
-    // used to close other popovers that are already showing.
-    document.PopoverStack().back()->HidePopoverInternal(
-        focus_behavior, transition_behavior,
-        /*exception_state=*/nullptr);
+  } else {
+    DCHECK_EQ(endpoint->PopoverType(), PopoverValueType::kAuto);
+    const Element* hint_ancestor = nullptr;
+    if (document.PopoverHintShowing()) {
+      // If there is a popover=hint showing that is a descendant of something on
+      // the popover=auto stack, then the hint should be hidden before that
+      // ancestor is hidden, regardless of popover_independence.
+      hint_ancestor =
+          FindTopmostPopoverAncestor(*document.PopoverHintShowing());
+      if (!hint_ancestor &&
+          popover_independence == HidePopoverIndependence::kHideUnrelated) {
+        document.PopoverHintShowing()->HidePopoverInternal(
+            focus_behavior, transition_behavior, exception_state);
+      }
+    }
+    // Then hide everything in the popover=auto stack until the last_to_hide
+    // popover is closed, or the stack is empty.
+    const HTMLElement* last_to_hide = nullptr;
+    bool found_endpoint = false;
+    for (auto popover : document.PopoverStack()) {
+      if (popover == endpoint) {
+        found_endpoint = true;
+      } else if (found_endpoint) {
+        last_to_hide = popover;
+        break;
+      }
+    }
+    if (!found_endpoint) {
+      return close_all_open_popovers();
+    }
+    if (!last_to_hide && document.PopoverHintShowing() &&
+        hint_ancestor == endpoint) {
+      DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+      // endpoint is the top of the popover stack, and there's a nested hint.
+      document.PopoverHintShowing()->HidePopoverInternal(
+          focus_behavior, transition_behavior, exception_state);
+    }
+    while (last_to_hide && last_to_hide->popoverOpen() &&
+           !document.PopoverStack().empty()) {
+      // We never throw exceptions from HideAllPopoversUntil, since it is always
+      // used to close other popovers that are already showing.
+      if (document.PopoverStack().back() == hint_ancestor) {
+        DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+        document.PopoverHintShowing()->HidePopoverInternal(
+            focus_behavior, transition_behavior, exception_state);
+      }
+      document.PopoverStack().back()->HidePopoverInternal(
+          focus_behavior, transition_behavior, exception_state);
+    }
   }
 }
 
@@ -1568,15 +1669,16 @@
   }
 
   auto& document = GetDocument();
-  if (PopoverType() == PopoverValueType::kAuto) {
+  if (PopoverType() == PopoverValueType::kAuto ||
+      PopoverType() == PopoverValueType::kHint) {
     // Hide any popovers above us in the stack.
-    HideAllPopoversUntil(this, document, focus_behavior, transition_behavior);
-
+    HideAllPopoversUntil(this, document, focus_behavior, transition_behavior,
+                         HidePopoverIndependence::kLeaveUnrelated);
     // The 'beforetoggle' event handlers could have changed this popover, e.g.
     // by changing its type, removing it from the document, or calling
     // hidePopover().
     auto& stack = document.PopoverStack();
-    if (!stack.Contains(this)) {
+    if (!stack.Contains(this) && this != document.PopoverHintShowing()) {
       if (exception_state) {
         exception_state->ThrowDOMException(
             DOMExceptionCode::kInvalidStateError,
@@ -1587,9 +1689,15 @@
     }
 
     // Then remove this popover from the stack.
-    DCHECK(!stack.empty());
-    DCHECK_EQ(stack.back(), this);
-    stack.pop_back();
+    if (PopoverType() == PopoverValueType::kAuto) {
+      DCHECK(!stack.empty());
+      DCHECK_EQ(stack.back(), this);
+      stack.pop_back();
+    } else {
+      DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+      DCHECK_EQ(document.TopmostPopoverOrHint(), this);
+      document.SetPopoverHintShowing(nullptr);
+    }
   }
 
   GetPopoverData()->setInvoker(nullptr);
@@ -1684,7 +1792,7 @@
 
   // If the popover does not use autofocus, then the focus should remain on the
   // currently active element.
-  // https://open-ui.org/components/popup.research.explainer#focus-management
+  // https://open-ui.org/components/popover.research.explainer#focus-management
   if (!control)
     return;
 
@@ -1775,10 +1883,12 @@
 // from the (possibly cyclic) graph of connections. For example, if two popovers
 // have anchors pointing to each other, the only valid relationship is that the
 // first one to open is the "parent" and the second is the "child". Only
-// popover=auto popovers are considered.
+// popover=auto popovers can *be* ancestors, and only popover=auto/hint popovers
+// can *have* ancestors.
 const HTMLElement* HTMLElement::FindTopmostPopoverAncestor(
     HTMLElement& new_popover) {
   DCHECK(new_popover.HasPopoverAttribute());
+  DCHECK_NE(new_popover.PopoverType(), PopoverValueType::kManual);
   auto& document = new_popover.GetDocument();
   DCHECK(RuntimeEnabledFeatures::HTMLPopoverAttributeEnabled(
       document.GetExecutionContext()));
@@ -1789,6 +1899,9 @@
   for (auto popover : document.PopoverStack()) {
     popover_positions.Set(popover, indx++);
   }
+  if (auto* popover_hint = document.PopoverHintShowing()) {
+    popover_positions.Set(popover_hint, indx++);
+  }
   popover_positions.Set(&new_popover, indx++);
 
   const HTMLElement* topmost_popover_ancestor = nullptr;
@@ -1797,8 +1910,10 @@
     if (!candidate)
       return;
     auto* candidate_ancestor = NearestInclusiveOpenPopover(candidate);
-    if (!candidate_ancestor)
+    if (!candidate_ancestor ||
+        candidate_ancestor->PopoverType() != PopoverValueType::kAuto) {
       return;
+    }
     int candidate_position = popover_positions.at(candidate_ancestor);
     if (!topmost_popover_ancestor ||
         popover_positions.at(topmost_popover_ancestor) < candidate_position) {
@@ -1837,6 +1952,9 @@
   auto* clicked_popover = NearestInclusiveOpenPopover(&node);
   auto* invoker_popover = NearestInclusiveTargetPopoverForInvoker(&node);
   auto get_stack_position = [&document](const HTMLElement* popover) {
+    if (popover && popover == document.PopoverHintShowing()) {
+      return document.PopoverStack().size() + 1;
+    }
     auto pos = document.PopoverStack().Find(popover);
     return pos == kNotFound ? 0 : (pos + 1);
   };
@@ -1852,10 +1970,12 @@
   DCHECK(event.isTrusted());
   auto& document = target_node.GetDocument();
   if (!RuntimeEnabledFeatures::HTMLPopoverAttributeEnabled(
-          document.GetExecutionContext()))
+          document.GetExecutionContext())) {
     return;
-  if (!document.TopmostPopover())
+  }
+  if (!document.TopmostPopoverOrHint()) {
     return;
+  }
 
   const AtomicString& event_type = event.type();
   if (IsA<PointerEvent>(event)) {
@@ -1885,7 +2005,8 @@
       if (same_target) {
         HideAllPopoversUntil(
             ancestor_popover, document, HidePopoverFocusBehavior::kNone,
-            HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions);
+            HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+            HidePopoverIndependence::kHideUnrelated);
       }
     }
   } else if (event_type == event_type_names::kKeydown) {
@@ -1893,8 +2014,8 @@
     if (key_event && key_event->key() == "Escape") {
       DCHECK(!event.GetEventPath().IsEmpty());
       DCHECK_EQ(Event::PhaseType::kNone, event.eventPhase());
-      // Escape key just pops the topmost popover off the stack.
-      document.TopmostPopover()->HidePopoverInternal(
+      // Escape key just pops the topmost popover=auto/hint off the stack.
+      document.TopmostPopoverOrHint()->HidePopoverInternal(
           HidePopoverFocusBehavior::kFocusPreviousElement,
           HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
           /*exception_state=*/nullptr);
@@ -1984,7 +2105,8 @@
       GetDocument().AddConsoleMessage(console_message);
       HTMLElement::HideAllPopoversUntil(
           nullptr, GetDocument(), HidePopoverFocusBehavior::kNone,
-          HidePopoverTransitionBehavior::kNoEventsNoWaiting);
+          HidePopoverTransitionBehavior::kNoEventsNoWaiting,
+          HidePopoverIndependence::kHideUnrelated);
       return;
     }
   }
diff --git a/third_party/blink/renderer/core/html/html_element.h b/third_party/blink/renderer/core/html/html_element.h
index b0569437..3865660f 100644
--- a/third_party/blink/renderer/core/html/html_element.h
+++ b/third_party/blink/renderer/core/html/html_element.h
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/text/text_direction.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -62,9 +63,11 @@
 enum class PopoverValueType {
   kNone,
   kAuto,
+  kHint,
   kManual,
 };
 constexpr const char* kPopoverTypeValueAuto = "auto";
+constexpr const char* kPopoverTypeValueHint = "hint";
 constexpr const char* kPopoverTypeValueManual = "manual";
 
 enum class PopoverTriggerAction {
@@ -84,6 +87,11 @@
   kNoEventsNoWaiting,
 };
 
+enum class HidePopoverIndependence {
+  kLeaveUnrelated,
+  kHideUnrelated,
+};
+
 class CORE_EXPORT HTMLElement : public Element {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -211,6 +219,9 @@
   // Popover API related functions.
   void UpdatePopoverAttribute(String);
   bool HasPopoverAttribute() const;
+  // The IDL reflections:
+  AtomicString popover() const;
+  void setPopover(const AtomicString& value);
   PopoverValueType PopoverType() const;
   bool popoverOpen() const;
   bool IsPopoverReady(PopoverTriggerAction action,
@@ -244,7 +255,8 @@
   static void HideAllPopoversUntil(const HTMLElement*,
                                    Document&,
                                    HidePopoverFocusBehavior,
-                                   HidePopoverTransitionBehavior);
+                                   HidePopoverTransitionBehavior,
+                                   HidePopoverIndependence);
   // This function checks that the ancestor relationships are still valid for
   // the entire popover stack. These can change in various ways, such as a
   // triggering element changing its `disabled` attribute. If any relationships
diff --git a/third_party/blink/renderer/core/html/html_element.idl b/third_party/blink/renderer/core/html/html_element.idl
index 24336eb..92a66a3 100644
--- a/third_party/blink/renderer/core/html/html_element.idl
+++ b/third_party/blink/renderer/core/html/html_element.idl
@@ -69,7 +69,8 @@
     [MeasureAs=ElementTogglePopover,RuntimeEnabled=HTMLPopoverAttribute,RaisesException] void togglePopover(optional boolean force);
     [MeasureAs=ElementShowPopover,RuntimeEnabled=HTMLPopoverAttribute,RaisesException] void showPopover();
     [MeasureAs=ElementHidePopover,RuntimeEnabled=HTMLPopoverAttribute,RaisesException] void hidePopover();
-    [CEReactions,RuntimeEnabled=HTMLPopoverAttribute,Reflect,ReflectOnly=("auto","manual"),ReflectEmpty="auto",ReflectInvalid="manual"] attribute DOMString? popover;
+//    [CEReactions,RuntimeEnabled=HTMLPopoverAttribute,Reflect,ReflectOnly=("auto","hint","manual"),ReflectEmpty="auto",ReflectInvalid="manual"] attribute DOMString? popover;
+    [CEReactions,RuntimeEnabled=HTMLPopoverAttribute] attribute DOMString? popover;
 
     // Used by both Anchor Positioning and Popover
     [CEReactions,RuntimeEnabled=CSSAnchorPositioning] attribute Element? anchorElement;
diff --git a/third_party/blink/renderer/core/html/html_frame_element_base.cc b/third_party/blink/renderer/core/html/html_frame_element_base.cc
index d8df68e..ca9855c 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element_base.cc
@@ -93,7 +93,6 @@
 
   KURL url = GetDocument().CompleteURL(url_);
 
-  WillPerformContainerInitiatedNavigation(url);
   // There is no (easy) way to tell if |url_| is relative at this point. That
   // is determined in the KURL constructor. If we fail to create an absolute
   // URL at this point, *and* the base URL is a data URL, assume |url_| was
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 0edf7c8..3f5cdaf 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -482,7 +482,7 @@
 void HTMLFrameOwnerElement::AddResourceTiming(
     mojom::blink::ResourceTimingInfoPtr info) {
   // Resource timing info should only be reported if the subframe is attached.
-  DCHECK(ContentFrame() && ContentFrame()->IsLocalFrame());
+  DCHECK(ContentFrame());
 
   // Make sure we don't double-report, e.g. in the case of restored iframes.
   if (!HasPendingFallbackTimingInfo()) {
@@ -512,17 +512,6 @@
   fallback_timing_info_.reset();
 }
 
-void HTMLFrameOwnerElement::WillPerformContainerInitiatedNavigation(
-    const KURL& url) {
-  if (!url.ProtocolIsInHTTPFamily() &&
-      !url.ProtocolIs(url::kUuidInPackageScheme)) {
-    return;
-  }
-
-  fallback_timing_info_ = CreateResourceTimingInfo(base::TimeTicks::Now(), url,
-                                                   /*response=*/nullptr);
-}
-
 // This will report fallback timing only if the "real" resource timing had not
 // been previously reported: e.g. a cross-origin iframe without TAO.
 void HTMLFrameOwnerElement::ReportFallbackResourceTimingIfNeeded() {
@@ -704,6 +693,15 @@
     bool replace_current_item) {
   TRACE_EVENT0("navigation", "HTMLFrameOwnerElement::LoadOrRedirectSubframe");
 
+  // If the subframe navigation is aborted or TAO fails, we report a "fallback"
+  // entry that starts at navigation and ends at load/error event.
+  if (url.ProtocolIsInHTTPFamily() ||
+      url.ProtocolIs(url::kUuidInPackageScheme)) {
+    fallback_timing_info_ =
+        CreateResourceTimingInfo(base::TimeTicks::Now(), url,
+                                 /*response=*/nullptr);
+  }
+
   // Update the |should_lazy_load_children_| value according to the "loading"
   // attribute immediately, so that it still gets respected even if the "src"
   // attribute gets parsed in ParseAttribute() before the "loading" attribute
@@ -730,6 +728,7 @@
 
   if (ContentFrame()) {
     FrameLoadRequest frame_load_request(GetDocument().domWindow(), request);
+    frame_load_request.SetIsContainerInitiated(true);
     frame_load_request.SetClientRedirectReason(
         ClientNavigationReason::kFrameNavigation);
     WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
@@ -790,6 +789,7 @@
   }
 
   FrameLoadRequest frame_load_request(GetDocument().domWindow(), request);
+  frame_load_request.SetIsContainerInitiated(true);
   child_frame->Loader().StartNavigation(frame_load_request, child_load_type);
 
   return true;
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index ca27f17..55d4d6f5 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -124,8 +124,6 @@
   void DidReportResourceTiming();
   bool HasPendingFallbackTimingInfo() const;
 
-  void WillPerformContainerInitiatedNavigation(const KURL&);
-
   // For unit tests, manually trigger the UpdateContainerPolicy method.
   void UpdateContainerPolicyForTests() { UpdateContainerPolicy(); }
 
@@ -191,6 +189,7 @@
   // based on the frame attributes.
   virtual network::mojom::blink::TrustTokenParamsPtr ConstructTrustTokenParams()
       const;
+  void ReportFallbackResourceTimingIfNeeded();
 
  protected:
   bool is_swapping_frames() const { return is_swapping_frames_; }
@@ -237,7 +236,6 @@
       bool is_loading_attr_lazy,
       AutomaticLazyLoadReason auto_lazy_load_reason);
 
-  void ReportFallbackResourceTimingIfNeeded();
   // Check if the frame should be lazy-loaded and apply when conditions are
   // passed. Return true when lazy-load is applied.
   bool LazyLoadIfPossible(const KURL&,
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index 9072e8b..111fdc2 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -631,8 +631,6 @@
       DCHECK(OwnedEmbeddedContentView());
     }
 
-    WillPerformContainerInitiatedNavigation(completed_url);
-
     // If the plugin element already contains a subframe,
     // loadOrRedirectSubframe will re-use it. Otherwise, it will create a
     // new frame and set it as the LayoutEmbeddedContent's EmbeddedContentView,
@@ -710,6 +708,7 @@
 }
 
 void HTMLPlugInElement::DispatchErrorEvent() {
+  ReportFallbackResourceTimingIfNeeded();
   if (IsA<PluginDocument>(GetDocument()) && GetDocument().LocalOwner()) {
     GetDocument().LocalOwner()->DispatchEvent(
         *Event::Create(event_type_names::kError));
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index 4ce14da..4070147 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -1332,6 +1332,17 @@
 #endif
     }
     stitched_block_size += NGFragment(writing_direction, walker).BlockSize();
+
+    // Repeated content isn't stitched, so just stop when we have processed one
+    // fragment.
+    if (walker.BreakToken() && walker.BreakToken()->IsRepeated()) {
+#if DCHECK_IS_ON()
+      // We haven't necessarily found |fragment|, but it doesn't matter in this
+      // case. Just silence the DHCECK below.
+      found_self = true;
+#endif
+      break;
+    }
   }
 #if DCHECK_IS_ON()
   DCHECK(found_self);
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 3f1f52b..4308beef 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -289,7 +289,6 @@
   CommitReason commit_reason;
   uint64_t main_resource_identifier;
   mojom::blink::ResourceTimingInfoPtr resource_timing_info_for_parent;
-  base::TimeTicks last_redirect_end_time;
   WebScopedVirtualTimePauser virtual_time_pauser;
   Member<PrefetchedSignedExchangeManager> prefetched_signed_exchange_manager;
   ukm::SourceId ukm_source_id;
@@ -314,6 +313,7 @@
   absl::optional<FencedFrame::RedactedFencedFrameProperties>
       fenced_frame_properties;
   bool has_storage_access;
+  mojom::blink::ParentResourceTimingAccess parent_resource_timing_access;
 };
 
 // Asserts size of DocumentLoader, so that whenever a new attribute is added to
@@ -513,7 +513,8 @@
       reduced_accept_language_(params_->reduced_accept_language),
       navigation_delivery_type_(params_->navigation_delivery_type),
       view_transition_state_(std::move(params_->view_transition_state)),
-      has_storage_access_(params_->has_storage_access) {
+      has_storage_access_(params_->has_storage_access),
+      parent_resource_timing_access_(params_->parent_resource_timing_access) {
   DCHECK(frame_);
   DCHECK(params_);
 
@@ -533,6 +534,8 @@
   document_policy_ = CreateDocumentPolicy();
 
   WebNavigationTimings& timings = params_->navigation_timings;
+  parent_resource_timing_access_ = timings.parent_resource_timing_access;
+
   if (!timings.input_start.is_null())
     document_load_timing_.SetInputStart(timings.input_start);
   if (timings.navigation_start.is_null()) {
@@ -826,7 +829,7 @@
       DCHECK(frame_->DomWindow());
       SoftNavigationHeuristics* heuristics =
           SoftNavigationHeuristics::From(*frame_->DomWindow());
-      heuristics->SetBackForwardNavigationURL(script_state, new_url);
+      heuristics->SetAsyncSoftNavigationURL(script_state, new_url);
     }
   }
   SinglePageAppNavigationType single_page_app_navigation_type =
@@ -1099,6 +1102,7 @@
     const absl::optional<WebURLError>& error) {
   TRACE_EVENT0("loading", "DocumentLoader::BodyLoadingFinished");
 
+  DCHECK(frame_);
   if (!error) {
     GetFrameLoader().Progress().CompleteProgress(main_resource_identifier_);
     probe::DidFinishLoading(
@@ -1246,10 +1250,6 @@
       probe::ToCoreProbeSink(GetFrame()), main_resource_identifier_, this,
       url_after_redirect, http_method_, http_body_.get());
 
-  if (ResourceLoadTiming* timing = redirect_response.GetResourceLoadTiming()) {
-    redirect_end_time_ = timing->ReceiveHeadersEnd();
-  }
-
   DCHECK(!GetTiming().FetchStart().is_null());
   GetTiming().AddRedirect(url_before_redirect, url_after_redirect);
 }
@@ -1884,11 +1884,7 @@
 
   WarnIfSandboxIneffective(document->domWindow());
 
-  if (view_transition_state_) {
-    ViewTransitionSupplement::CreateFromSnapshotForNavigation(
-        *document, std::move(*view_transition_state_));
-    view_transition_state_.reset();
-  }
+  StartViewTransitionIfNeeded(*document);
 }
 
 void DocumentLoader::WillCommitNavigation() {
@@ -2344,40 +2340,30 @@
   frame_->DomWindow()->SetPolicyContainer(std::move(policy_container_));
   frame_->DomWindow()->SetContentSecurityPolicy(csp);
 
+  BlinkStorageKey storage_key(storage_key_);
+  // TODO(crbug.com/1199077): For some reason `storage_key_` is occasionally
+  // null. If that's the case this will create one based on the
+  // `security_origin`.
+  // TODO(crbug.com/1199077): Some tests (potentially other code?) rely on an
+  // opaque origin + nonce. Investigate whether this combination should be
+  // disallowed.
+  if (storage_key.GetSecurityOrigin()->IsOpaque() && !storage_key.GetNonce()) {
+    storage_key = BlinkStorageKey::CreateFirstParty(security_origin);
+  }
+
   // Now that we have the final window and Agent, ensure the security origin has
   // the appropriate agent cluster id. This may derive a new security origin.
   security_origin = security_origin->GetOriginForAgentCluster(
       frame_->DomWindow()->GetAgent()->cluster_id());
 
-  if (storage_key_.GetNonce()) {
-    // If the nonce isn't null, we can use the simpler form of the constructor.
-    frame_->DomWindow()->SetStorageKey(BlinkStorageKey::CreateWithNonce(
-        security_origin, *storage_key_.GetNonce()));
-  } else {
-    // TODO(crbug.com/1159586): Remove this when 3psp storage is on. It's here
-    // to preserve the information that is stripped due to the key being
-    // re-made.
-    const auto& storage_key_with_3psp =
-        storage_key_.CopyWithForceEnabledThirdPartyStoragePartitioning();
-    BlinkSchemefulSite top_level_site = storage_key_with_3psp.GetTopLevelSite();
-    // If `security_origin` is opaque or does not match `top_level_site` we
-    // must ensure `ancestor_chain_bit` is kCrossSite.
-    mojom::blink::AncestorChainBit ancestor_chain_bit =
-        storage_key_with_3psp.GetAncestorChainBit();
-    if (security_origin->IsOpaque() ||
-        BlinkSchemefulSite(security_origin) != top_level_site) {
-      ancestor_chain_bit = mojom::blink::AncestorChainBit::kCrossSite;
-    }
-    // TODO(https://crbug.com/888079): Just use the storage key sent by the
-    // browser once the browser will be able to compute the origin in all cases.
-    frame_->DomWindow()->SetStorageKey(BlinkStorageKey::Create(
-        security_origin, top_level_site, ancestor_chain_bit));
-  }
+  // TODO(https://crbug.com/888079): Just use the storage key sent by the
+  // browser once the browser will be able to compute the origin in all cases.
+  frame_->DomWindow()->SetStorageKey(storage_key.WithOrigin(security_origin));
 
-  if (storage_key_ == session_storage_key_ ||
-      storage_key_.GetSecurityOrigin()->IsOpaque() ||
+  if (storage_key == session_storage_key_ ||
+      storage_key.GetSecurityOrigin()->IsOpaque() ||
       session_storage_key_.GetSecurityOrigin()->IsOpaque()) {
-    // If the `storage_key_` and `session_storage_key_` match (or either are
+    // If the `storage_key` and `session_storage_key_` match (or either are
     // opaque), we should just use whatever storage key was built above as we
     // aren't preventing partition.
     frame_->DomWindow()->SetSessionStorageKey(
@@ -2640,27 +2626,32 @@
 
   if (response_.ShouldPopulateResourceTiming() ||
       is_error_page_for_failed_navigation_) {
-    // We only report resource timing to the parent if:
-    // 1. We have a parent (owner)
-    // 2. Timing Allow Passed - otherwise we report fallback timing.
-    // 3. It's an external navigation (kWebNavigationTypeOther)
-    // TODO (crbug.com/1410705): Using navigation_type_ for this covers
-    // most cases but might still have very rare racy edge cases, such as
-    // extension or window.open with target cancelling an ongoing navigation
-    // and start a new navigation to the same URL.
-    if (frame_->Owner() && response_.TimingAllowPassed() &&
-        navigation_type_ == WebNavigationType::kWebNavigationTypeOther) {
-      resource_timing_info_for_parent_ = CreateResourceTimingInfo(
-          GetTiming().NavigationStart(), original_url_, &response_);
-      if (!is_same_origin_initiator ||
-          document_load_timing_.HasCrossOriginRedirect()) {
-        resource_timing_info_for_parent_->content_type = g_empty_string;
-        resource_timing_info_for_parent_->response_status = 0;
+    // We only report resource timing info to the parent if:
+    // 1. The navigation is container-initiated (e.g. iframe changed src)
+    // 2. TAO passed.
+    if (parent_resource_timing_access_ !=
+            mojom::blink::ParentResourceTimingAccess::kDoNotReport &&
+        response_.TimingAllowPassed()) {
+      ResourceResponse response_for_parent(response_);
+      if (parent_resource_timing_access_ ==
+          mojom::blink::ParentResourceTimingAccess::
+              kReportWithoutResponseDetails) {
+        response_for_parent.SetType(network::mojom::FetchResponseType::kOpaque);
       }
+
+      DCHECK(frame_->Owner());
+      DCHECK(GetRequestorOrigin());
+      resource_timing_info_for_parent_ = CreateResourceTimingInfo(
+          GetTiming().NavigationStart(), original_url_, &response_for_parent);
+
       resource_timing_info_for_parent_->last_redirect_end_time =
-          redirect_end_time_;
+          document_load_timing_.RedirectEnd();
     }
 
+    // TimingAllowPassed only applies to resource
+    // timing reporting. Navigation timing is always same-origin with the
+    // document that holds to the timing entry, as navigation timing represents
+    // the timing of that document itself.
     response_.SetTimingAllowPassed(true);
     mojom::blink::ResourceTimingInfoPtr navigation_timing_info =
         CreateResourceTimingInfo(base::TimeTicks(),
@@ -2668,8 +2659,9 @@
                                      ? pre_redirect_url_for_failed_navigations_
                                      : url_,
                                  &response_);
-    navigation_timing_info->last_redirect_end_time = redirect_end_time_;
-    DCHECK(frame_);
+    navigation_timing_info->last_redirect_end_time =
+        document_load_timing_.RedirectEnd();
+
     DCHECK(frame_->DomWindow());
     DOMWindowPerformance::performance(*frame_->DomWindow())
         ->CreateNavigationTimingInstance(std::move(navigation_timing_info));
@@ -3101,6 +3093,10 @@
   }
 
   GetTiming().SetActivationStart(params.activation_start);
+
+  DCHECK(!view_transition_state_);
+  view_transition_state_ = std::move(params.view_transition_state);
+  StartViewTransitionIfNeeded(*frame_->GetDocument());
 }
 
 HashMap<KURL, EarlyHintsPreloadEntry>
@@ -3272,6 +3268,14 @@
   };
 }
 
+void DocumentLoader::StartViewTransitionIfNeeded(Document& document) {
+  if (view_transition_state_) {
+    ViewTransitionSupplement::CreateFromSnapshotForNavigation(
+        document, std::move(*view_transition_state_));
+    view_transition_state_.reset();
+  }
+}
+
 // static
 void DocumentLoader::DisableCodeCacheForTesting() {
   GetDisableCodeCacheForTesting() = true;
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index dc759a0..11e3bce 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -49,6 +49,8 @@
 #include "third_party/blink/public/mojom/loader/content_security_notifier.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/loader/same_document_navigation_type.mojom-blink.h"
+#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-shared.h"
 #include "third_party/blink/public/mojom/page/page.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/page_state/page_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink.h"
@@ -601,6 +603,11 @@
   ContentSecurityPolicy* CreateCSP();
 
   bool IsSameOriginInitiator() const;
+
+  // This initiates a view transition if the `view_transition_state_` has been
+  // specified.
+  void StartViewTransitionIfNeeded(Document& document);
+
   // Params are saved in constructor and are cleared after StartLoading().
   // TODO(dgozman): remove once StartLoading is merged with constructor.
   std::unique_ptr<WebNavigationParams> params_;
@@ -753,7 +760,6 @@
   CommitReason commit_reason_ = CommitReason::kRegular;
   uint64_t main_resource_identifier_ = 0;
   mojom::blink::ResourceTimingInfoPtr resource_timing_info_for_parent_;
-  base::TimeTicks redirect_end_time_;
   WebScopedVirtualTimePauser virtual_time_pauser_;
   Member<PrefetchedSignedExchangeManager> prefetched_signed_exchange_manager_;
   ukm::SourceId ukm_source_id_;
@@ -817,6 +823,10 @@
   // Indicates whether the document should be loaded with its has_storage_access
   // bit set.
   const bool has_storage_access_;
+
+  // Only container-initiated navigations (e.g. iframe change src) report
+  // their resource timing to the parent.
+  mojom::blink::ParentResourceTimingAccess parent_resource_timing_access_;
 };
 
 DECLARE_WEAK_IDENTIFIER_MAP(DocumentLoader);
diff --git a/third_party/blink/renderer/core/loader/empty_clients.cc b/third_party/blink/renderer/core/loader/empty_clients.cc
index 1547749..3047062 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.cc
+++ b/third_party/blink/renderer/core/loader/empty_clients.cc
@@ -121,7 +121,8 @@
     const absl::optional<Impression>&,
     const LocalFrameToken* initiator_frame_token,
     std::unique_ptr<SourceLocation>,
-    mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>) {}
+    mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>,
+    bool is_container_initiated) {}
 
 void EmptyLocalFrameClient::DispatchWillSendSubmitEvent(HTMLFormElement*) {}
 
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 84e9886..9e34e80 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -308,8 +308,8 @@
       const absl::optional<Impression>&,
       const LocalFrameToken* initiator_frame_token,
       std::unique_ptr<SourceLocation>,
-      mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>)
-      override;
+      mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>,
+      bool is_container_initiated) override;
 
   void DispatchWillSendSubmitEvent(HTMLFormElement*) override;
 
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h
index 5282d87..8d28e63 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.h
+++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -84,6 +84,10 @@
     return client_navigation_reason_;
   }
 
+  void SetIsContainerInitiated(bool value) { is_container_initiated_ = value; }
+
+  bool IsContainerInitiated() const { return is_container_initiated_; }
+
   NavigationPolicy GetNavigationPolicy() const { return navigation_policy_; }
   void SetNavigationPolicy(NavigationPolicy navigation_policy) {
     navigation_policy_ = navigation_policy;
@@ -240,6 +244,10 @@
 
   mojom::blink::ForceHistoryPush force_history_push_ =
       mojom::blink::ForceHistoryPush::kNo;
+
+  // Only container-initiated navigations (e.g. iframe change src) report a
+  // resource timing entry to the parent.
+  bool is_container_initiated_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 8bad0fba..939b5977b 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -852,7 +852,8 @@
       request.GetInputStartTime(), request.HrefTranslate().GetString(),
       request.Impression(), request.GetInitiatorFrameToken(),
       request.TakeSourceLocation(),
-      request.TakeInitiatorPolicyContainerKeepAliveHandle());
+      request.TakeInitiatorPolicyContainerKeepAliveHandle(),
+      request.IsContainerInitiated());
 }
 
 static void FillStaticResponseIfNeeded(WebNavigationParams* params,
diff --git a/third_party/blink/renderer/core/navigation_api/navigate_event.cc b/third_party/blink/renderer/core/navigation_api/navigate_event.cc
index 5c9824b..6fb51d1 100644
--- a/third_party/blink/renderer/core/navigation_api/navigate_event.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigate_event.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/navigation_api/navigation_destination.h"
+#include "third_party/blink/renderer/core/timing/soft_navigation_heuristics.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
@@ -150,6 +151,19 @@
       state_object, dispatch_params_->frame_load_type,
       dispatch_params_->is_browser_initiated,
       dispatch_params_->is_synchronously_committed_same_document);
+
+  // This is considered a soft navigation URL change at this point, when the
+  // user visible URL change happens. Skip the descendant check because the URL
+  // change doesn't happen in a JS task.
+  auto* soft_navigation_heuristics =
+      DomWindow() ? SoftNavigationHeuristics::From(*DomWindow()) : nullptr;
+  if (soft_navigation_heuristics && user_initiated_ && !download_request_) {
+    auto* script_state = ToScriptStateForMainWorld(DomWindow()->GetFrame());
+    ScriptState::Scope scope(script_state);
+    soft_navigation_heuristics->SawURLChange(script_state,
+                                             dispatch_params_->url,
+                                             /*skip_descendant_check=*/true);
+  }
 }
 
 void NavigateEvent::FinalizeNavigationActionPromisesList() {
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
index a63be49..5eb87252 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -858,18 +858,6 @@
     transition_ = MakeGarbageCollected<NavigationTransition>(
         script_state, navigation_type, currentEntry());
     navigate_event->DoCommit();
-
-    // This is considered a soft navigation URL change at this point, when the
-    // user visible URL change happens, and before the interception handler
-    // runs. We're skipping the descendant check because the the URL change
-    // doesn't happen in a JS task, and we know this URL change is related to
-    // the user initiated click event from the fact that
-    // `soft_navigation_scope` is not nullptr.
-    if (soft_navigation_scope) {
-      soft_navigation_heuristics->SawURLChange(script_state,
-                                               /*url=*/params->url,
-                                               /*skip_descendant_check=*/true);
-    }
   }
 
   if (navigate_event->HasNavigationActions() ||
diff --git a/third_party/blink/renderer/core/paint/image_painter.cc b/third_party/blink/renderer/core/paint/image_painter.cc
index 9ca07ff..3efa2233 100644
--- a/third_party/blink/renderer/core/paint/image_painter.cc
+++ b/third_party/blink/renderer/core/paint/image_painter.cc
@@ -215,10 +215,11 @@
                                 paint_offset);
     context.SetStrokeStyle(kSolidStroke);
     context.SetStrokeColor(Color::kLightGray);
-    context.SetFillColor(Color::kTransparent);
-    context.DrawRect(ToPixelSnappedRect(content_rect),
-                     PaintAutoDarkMode(layout_image_.StyleRef(),
-                                       DarkModeFilter::ElementRole::kBorder));
+    gfx::RectF outline_rect(ToPixelSnappedRect(content_rect));
+    outline_rect.Inset(0.5f);
+    context.StrokeRect(outline_rect, 1,
+                       PaintAutoDarkMode(layout_image_.StyleRef(),
+                                         DarkModeFilter::ElementRole::kBorder));
     return;
   }
 
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 9de1b30..15516e1 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -476,7 +476,11 @@
   // fragment of an OOF fragment is always simply the parent.
   if (!context.current_container.IsInFragmentationContext() ||
       (fragment && fragment->IsMonolithic())) {
-    context.current_container.fragment = fragment;
+    // Anonymous blocks are not allowed to be containing blocks, so we should
+    // skip over any such elements.
+    if (!fragment || !fragment->IsAnonymousBlock()) {
+      context.current_container.fragment = fragment;
+    }
   }
 
   if (!object.CanContainAbsolutePositionObjects())
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
index 0c5642f..eb3ad77c 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
@@ -63,11 +63,13 @@
     larger_corner.set_size(
         gfx::Size(larger_corner.width() + 1, larger_corner.height() + 1));
     context.SetStrokeColor(Color(217, 217, 217));
-    context.SetStrokeThickness(1.0f);
-    context.SetFillColor(Color::kTransparent);
-    AutoDarkMode auto_dark_mode(PaintAutoDarkMode(
-        box->StyleRef(), DarkModeFilter::ElementRole::kBackground));
-    context.DrawRect(larger_corner, auto_dark_mode);
+    context.SetStrokeStyle(kSolidStroke);
+    gfx::RectF corner_outline(larger_corner);
+    corner_outline.Inset(0.5f);
+    context.StrokeRect(
+        corner_outline, 1,
+        PaintAutoDarkMode(box->StyleRef(),
+                          DarkModeFilter::ElementRole::kBackground));
   }
 }
 
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
index de05bfe..72dedaa7 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -117,20 +117,6 @@
   Referrer referrer =
       SecurityPolicy::GenerateReferrer(referrer_policy, url, outgoing_referrer);
 
-  // TODO(mcnee): Speculation rules initially shipped with a bug where a policy
-  // of "no-referrer" would be assumed and the referrer policy restriction was
-  // not enforced. We emulate that behaviour here as sites did not have a means
-  // of specifying a suitable policy. SpeculationRulesReferrerPolicyKey shipped
-  // in M111. This workaround should be removed when the flag is removed.
-  // See https://crbug.com/1398772.
-  if (!RuntimeEnabledFeatures::SpeculationRulesReferrerPolicyKeyEnabled(
-          execution_context) &&
-      !AcceptableReferrerPolicy(referrer, is_initially_same_site)) {
-    referrer = SecurityPolicy::GenerateReferrer(
-        network::mojom::ReferrerPolicy::kNever, url, outgoing_referrer);
-    DCHECK(AcceptableReferrerPolicy(referrer, is_initially_same_site));
-  }
-
   if (!AcceptableReferrerPolicy(referrer, is_initially_same_site)) {
     auto* console_message = MakeGarbageCollected<ConsoleMessage>(
         mojom::blink::ConsoleMessageSource::kOther,
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
index 97559fd..e6fc4411 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
@@ -66,16 +66,14 @@
                                       String* out_error) {
   // https://wicg.github.io/nav-speculation/speculation-rules.html#parse-a-speculation-rule
 
-  // If input has any key other than "source", "urls", "requires", "target_hint"
-  // and "relative_to", then return null.
-  const char* const kKnownKeys[] = {"source",      "urls",  "requires",
-                                    "target_hint", "where", "relative_to"};
+  // If input has any key other than "source", "urls", "where", "requires",
+  // "target_hint", "referrer_policy", "relative_to", and "eagerness", then
+  // return null.
+  const char* const kKnownKeys[] = {
+      "source",          "urls",       "where", "requires", "target_hint",
+      "referrer_policy", "relative_to"};
   const auto kConditionalKnownKeys = [context]() {
     Vector<const char*, 4> conditional_known_keys;
-    if (RuntimeEnabledFeatures::SpeculationRulesReferrerPolicyKeyEnabled(
-            context)) {
-      conditional_known_keys.push_back("referrer_policy");
-    }
     if (RuntimeEnabledFeatures::SpeculationRulesEagernessEnabled(context)) {
       conditional_known_keys.push_back("eagerness");
     }
@@ -268,13 +266,12 @@
     }
   }
 
+  // Let referrerPolicy be the empty string.
   absl::optional<network::mojom::ReferrerPolicy> referrer_policy;
+  // If input["referrer_policy"] exists:
   JSONValue* referrer_policy_value = input->Get("referrer_policy");
   if (referrer_policy_value) {
-    // Feature gated due to known keys check above.
-    DCHECK(RuntimeEnabledFeatures::SpeculationRulesReferrerPolicyKeyEnabled(
-        context));
-
+    // If input["referrer_policy"] is not a referrer policy, then return null.
     String referrer_policy_str;
     if (!referrer_policy_value->AsString(&referrer_policy_str)) {
       SetParseErrorMessage(out_error, "A referrer policy must be a string.");
@@ -293,6 +290,7 @@
         return nullptr;
       }
       DCHECK_NE(referrer_policy_out, network::mojom::ReferrerPolicy::kDefault);
+      // Set referrerPolicy to input["referrer_policy"].
       referrer_policy = referrer_policy_out;
     }
   }
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 0338383..41fd48d 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -205,10 +205,6 @@
   return *cached_data_->position_fallback_styles_;
 }
 
-scoped_refptr<ComputedStyle> ComputedStyle::Clone(const ComputedStyle& other) {
-  return base::AdoptRef(new ComputedStyle(PassKey(), other));
-}
-
 ALWAYS_INLINE ComputedStyle::ComputedStyle()
     : ComputedStyleBase(), RefCounted<ComputedStyle>() {}
 
@@ -2472,6 +2468,16 @@
   return element.IsInTopLayer() && TopLayer() == ETopLayer::kBrowser;
 }
 
+ComputedStyleBuilder::ComputedStyleBuilder(const ComputedStyle& style) {
+  style_ = base::AdoptRef(new ComputedStyle(style));
+  SetStyleBase(*style_);
+}
+
+scoped_refptr<const ComputedStyle> ComputedStyleBuilder::CloneStyle() const {
+  DCHECK(style_);
+  return base::AdoptRef(new ComputedStyle(*style_));
+}
+
 void ComputedStyleBuilder::PropagateIndependentInheritedProperties(
     const ComputedStyle& parent_style) {
   ComputedStyleBuilderBase::PropagateIndependentInheritedProperties(
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 73f0950e..6b0086b 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -340,9 +340,6 @@
   // into new instances.
   CORE_EXPORT static scoped_refptr<ComputedStyle> CreateInitialStyleSingleton();
 
-  // Shallow copy into a new instance sharing DataPtrs.
-  CORE_EXPORT static scoped_refptr<ComputedStyle> Clone(const ComputedStyle&);
-
   static const ComputedStyle* NullifyEnsured(const ComputedStyle* style) {
     if (!style) {
       return nullptr;
@@ -2779,10 +2776,7 @@
   // Access to UserModify().
   friend class MatchedPropertiesCache;
 
-  explicit ComputedStyleBuilder(const ComputedStyle& style) {
-    style_ = ComputedStyle::Clone(style);
-    SetStyleBase(*style_);
-  }
+  CORE_EXPORT explicit ComputedStyleBuilder(const ComputedStyle& style);
   ComputedStyleBuilder(const ComputedStyleBuilder& builder) = delete;
   ComputedStyleBuilder(ComputedStyleBuilder&&) = default;
   ComputedStyleBuilder& operator=(const ComputedStyleBuilder&) = delete;
@@ -2791,10 +2785,7 @@
   scoped_refptr<const ComputedStyle> TakeStyle() { return std::move(style_); }
 
   // NOTE: Prefer `TakeStyle()` if possible.
-  scoped_refptr<const ComputedStyle> CloneStyle() const {
-    DCHECK(style_);
-    return ComputedStyle::Clone(*style_);
-  }
+  CORE_EXPORT scoped_refptr<const ComputedStyle> CloneStyle() const;
 
   CORE_EXPORT void InheritFrom(
       const ComputedStyle& inherit_parent,
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index d8c35ea9..ea086d55 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -51,8 +51,8 @@
     initial_style_ = ComputedStyle::CreateInitialStyleSingleton();
   }
 
-  scoped_refptr<ComputedStyle> CreateComputedStyle() {
-    return ComputedStyle::Clone(*initial_style_);
+  scoped_refptr<const ComputedStyle> InitialComputedStyle() {
+    return initial_style_;
   }
 
   ComputedStyleBuilder CreateComputedStyleBuilder() {
@@ -118,7 +118,7 @@
 }
 
 TEST_F(ComputedStyleTest, LayoutContainmentStackingContext) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   EXPECT_FALSE(style->IsStackingContextWithoutContainment());
 
   ComputedStyleBuilder builder(*style);
@@ -210,7 +210,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesRespectsTransformAnimation) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentTransformAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -244,7 +244,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesRespectsScaleAnimation) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentScaleAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -256,7 +256,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesRespectsRotateAnimation) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentRotateAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -268,7 +268,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesRespectsTranslateAnimation) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentTranslateAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -280,7 +280,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsOpacity) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentOpacityAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -292,7 +292,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsFilter) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentFilterAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -304,7 +304,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsBackdropFilter) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetHasCurrentBackdropFilterAnimation(true);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -316,7 +316,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsBackfaceVisibility) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetBackfaceVisibility(EBackfaceVisibility::kHidden);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -328,7 +328,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsWillChange) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetBackfaceVisibility(EBackfaceVisibility::kHidden);
   builder.SetWillChangeProperties({CSSPropertyID::kOpacity});
@@ -357,7 +357,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsOverflow) {
-  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   builder.SetOverflowX(EOverflow::kHidden);
   scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
@@ -369,7 +369,7 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsContainsPaint) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   ComputedStyleBuilder builder(*style);
   // This induces a flat used transform style.
   builder.SetContain(kContainsPaint);
@@ -448,8 +448,8 @@
   builder.SetBorderTopStyle(EBorderStyle::kSolid);
   builder.SetBorderRightStyle(EBorderStyle::kSolid);
   builder.SetBorderBottomStyle(EBorderStyle::kSolid);
-  scoped_refptr<const ComputedStyle> style = builder.TakeStyle();
-  scoped_refptr<const ComputedStyle> other = ComputedStyle::Clone(*style);
+  scoped_refptr<const ComputedStyle> style = builder.CloneStyle();
+  scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
   EXPECT_TRUE(style->BorderSizeEquals(*other));
 
   UPDATE_STYLE(style, SetBorderLeftWidth, LayoutUnit(1));
@@ -545,7 +545,7 @@
     builder.Set##flag(true);                                               \
     auto style = builder.TakeStyle();                                      \
     EXPECT_TRUE(style->flag());                                            \
-    auto other = CreateComputedStyle();                                    \
+    auto other = InitialComputedStyle();                                   \
     EXPECT_FALSE(other->flag());                                           \
     EXPECT_EQ(ComputedStyle::Difference::inherited,                        \
               ComputedStyle::ComputeDifference(style.get(), other.get())); \
@@ -560,7 +560,7 @@
     builder.Set##flag(true);                                               \
     auto style = builder.TakeStyle();                                      \
     EXPECT_TRUE(style->flag());                                            \
-    auto other = CreateComputedStyle();                                    \
+    auto other = InitialComputedStyle();                                   \
     EXPECT_FALSE(other->flag());                                           \
     EXPECT_EQ(ComputedStyle::Difference::kEqual,                           \
               ComputedStyle::ComputeDifference(style.get(), other.get())); \
@@ -962,7 +962,7 @@
 }
 
 TEST_F(ComputedStyleTest, InitialVariableNamesEmpty) {
-  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   EXPECT_TRUE(style->GetVariableNames().empty());
 }
 
@@ -1061,7 +1061,7 @@
 }
 
 TEST_F(ComputedStyleTest, GetVariableNamesCount_Invalidation) {
-  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   EXPECT_EQ(style->GetVariableNamesCount(), 0u);
 
   auto data = css_test_helpers::CreateVariableData("foo");
@@ -1502,7 +1502,7 @@
                            nullptr /* StyleRecalcContext */,
                            StyleRequest(initial.get()));
 
-  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = InitialComputedStyle();
   state.SetStyle(*initial);
   EXPECT_FALSE(state.StyleBuilder().Animations());
   EXPECT_FALSE(state.StyleBuilder().Transitions());
@@ -1773,7 +1773,7 @@
 }
 
 TEST_F(ComputedStyleTest, BasicBuilder) {
-  scoped_refptr<ComputedStyle> original = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> original = InitialComputedStyle();
 
   Length left = Length::Fixed(1.0f);
   Length right = Length::Fixed(2.0f);
@@ -1794,7 +1794,7 @@
 TEST_F(ComputedStyleTest, MoveBuilder) {
   Length one = Length::Fixed(1.0f);
 
-  ComputedStyleBuilder builder1(*CreateComputedStyle());
+  ComputedStyleBuilder builder1(*InitialComputedStyle());
   builder1.SetScrollPaddingLeft(one);
 
   ComputedStyleBuilder builder2(std::move(builder1));
@@ -1809,10 +1809,10 @@
 TEST_F(ComputedStyleTest, MoveAssignBuilder) {
   Length one = Length::Fixed(1.0f);
 
-  ComputedStyleBuilder builder1(*CreateComputedStyle());
+  ComputedStyleBuilder builder1(*InitialComputedStyle());
   builder1.SetScrollPaddingLeft(one);
 
-  ComputedStyleBuilder builder2(*CreateComputedStyle());
+  ComputedStyleBuilder builder2(*InitialComputedStyle());
   builder2 = std::move(builder1);
 
   EXPECT_FALSE(builder1.TakeStyle());
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 2025225..727c8e6f 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -1139,6 +1139,9 @@
 
 ShadowRoot* Internals::shadowRoot(Element* host) {
   DCHECK(host);
+  if (auto* input = DynamicTo<HTMLInputElement>(*host)) {
+    input->EnsureShadowSubtree();
+  }
   return host->GetShadowRoot();
 }
 
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index f8cd29c..f5d79e56 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -709,17 +709,6 @@
   resource_timing_secondary_buffer_.push_back(entry);
 }
 
-void Performance::AddResourceTimingWithUnparsedServerTiming(
-    mojom::blink::ResourceTimingInfoPtr info,
-    const String& server_timing_value,
-    const AtomicString& initiator_type) {
-  if (info->allow_timing_details) {
-    info->server_timing =
-        ParseServerTimingFromHeaderValueToMojo(server_timing_value);
-  }
-  AddResourceTiming(std::move(info), initiator_type);
-}
-
 // Called after loadEventEnd happens.
 void Performance::NotifyNavigationTimingToObservers() {
   if (navigation_timing_)
diff --git a/third_party/blink/renderer/core/timing/performance.h b/third_party/blink/renderer/core/timing/performance.h
index 8909564f..4ab16f2 100644
--- a/third_party/blink/renderer/core/timing/performance.h
+++ b/third_party/blink/renderer/core/timing/performance.h
@@ -200,11 +200,6 @@
   void AddResourceTiming(mojom::blink::ResourceTimingInfoPtr,
                          const AtomicString& initiator_type);
 
-  void AddResourceTimingWithUnparsedServerTiming(
-      mojom::blink::ResourceTimingInfoPtr,
-      const String& server_timing_value,
-      const AtomicString& initiator_type);
-
   void NotifyNavigationTimingToObservers();
 
   void AddFirstPaintTiming(base::TimeTicks start_time,
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
index 6ed3d24..911fe65d 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
@@ -156,7 +156,7 @@
   SetIsTrackingSoftNavigationHeuristicsOnDocument(false);
 }
 
-void SoftNavigationHeuristics::SetBackForwardNavigationURL(
+void SoftNavigationHeuristics::SetAsyncSoftNavigationURL(
     ScriptState* script_state,
     const String& url) {
   if (!url_.empty()) {
@@ -178,11 +178,12 @@
   }
   LocalDOMWindow* window = frame->DomWindow();
   DCHECK(window);
-  // In case of a Soft Navigation using `history.back()`, `history.forward()` or
-  // `history.go()`, `SawURLChange` was called with an empty URL. If that's the
-  // case, don't report the Soft Navigation just yet, and wait for
-  // `SetBackForwardNavigationURL` to be called with the correct URL (which the
-  // renderer only knows about asynchronously).
+  // In case of a Soft Navigation where the url does not change immediately,
+  //`SawURLChange` was called with an empty URL. If that's the case, don't
+  // report the Soft Navigation just yet, and wait for
+  // `SetAsyncSoftNavigationURL` to be called with the correct URL (in the case
+  // of traversals, the renderer only knows about the correct URL asynchronously
+  // anyway).
   if (url_.empty()) {
     ResetPaintsIfNeeded(frame, window);
     return;
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
index 32db599..9d3193c 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
@@ -41,8 +41,7 @@
                     bool skip_descendant_check = false);
   void ModifiedDOM(ScriptState*);
   uint32_t SoftNavigationCount() { return soft_navigation_count_; }
-  void SetBackForwardNavigationURL(ScriptState* script_state,
-                                   const String& url);
+  void SetAsyncSoftNavigationURL(ScriptState* script_state, const String& url);
 
   // TaskAttributionTracker::Observer's implementation.
   void OnCreateTaskScope(const scheduler::TaskAttributionId&) override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 52c2f6a..972d38c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -103,6 +103,7 @@
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html/portal/html_portal_element.h"
 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
+#include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
@@ -3319,6 +3320,8 @@
       return ax::mojom::blink::IsPopup::kNone;
     case PopoverValueType::kAuto:
       return ax::mojom::blink::IsPopup::kAuto;
+    case PopoverValueType::kHint:
+      return ax::mojom::blink::IsPopup::kHint;
     case PopoverValueType::kManual:
       return ax::mojom::blink::IsPopup::kManual;
   }
@@ -5775,6 +5778,42 @@
     }
   }
 
+  // For form controls that act as triggering elements for popovers of type
+  // kHint, then set aria-describedby to the popover.
+  if (auto* form_control = DynamicTo<HTMLFormControlElement>(element)) {
+    auto popover_target = form_control->popoverTargetElement();
+    if (popover_target.popover &&
+        popover_target.popover->PopoverType() == PopoverValueType::kHint) {
+      DCHECK(RuntimeEnabledFeatures::HTMLPopoverHintEnabled());
+      description_from = ax::mojom::blink::DescriptionFrom::kPopoverAttribute;
+      if (description_sources) {
+        description_sources->push_back(DescriptionSource(
+            found_description, html_names::kPopovertargetAttr));
+        description_sources->back().type = description_from;
+      }
+      AXObject* popover_ax_object =
+          AXObjectCache().GetOrCreate(popover_target.popover);
+      if (popover_ax_object) {
+        AXObjectSet visited;
+        description = RecursiveTextAlternative(*popover_ax_object,
+                                               popover_ax_object, visited);
+        if (related_objects) {
+          related_objects->push_back(
+              MakeGarbageCollected<NameSourceRelatedObject>(popover_ax_object,
+                                                            description));
+        }
+        if (description_sources) {
+          DescriptionSource& source = description_sources->back();
+          source.related_objects = *related_objects;
+          source.text = description;
+          found_description = true;
+        } else {
+          return description;
+        }
+      }
+    }
+  }
+
   description_from = ax::mojom::blink::DescriptionFrom::kNone;
 
   if (found_description) {
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 1ff5e69..f1872ed 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -82,7 +82,6 @@
       error_code = GeolocationPositionError::kPositionUnavailable;
       break;
     case device::mojom::blink::Geoposition::ErrorCode::NONE:
-    case device::mojom::blink::Geoposition::ErrorCode::TIMEOUT:
       NOTREACHED();
       break;
   }
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index dd6e469..6e6a5da6 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -1694,14 +1694,8 @@
 }
 
 void WebGLRenderingContextBase::SetIsInHiddenPage(bool hidden) {
-  is_hidden_ = hidden;
   if (GetDrawingBuffer())
     GetDrawingBuffer()->SetIsInHiddenPage(hidden);
-
-  if (!hidden && isContextLost() && restore_allowed_ &&
-      auto_recovery_method_ == kAuto && !restore_timer_.IsActive()) {
-    restore_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
-  }
 }
 
 bool WebGLRenderingContextBase::PaintRenderingResultsToCanvas(
@@ -8547,13 +8541,11 @@
       WebGLContextEvent::Create(event_type_names::kWebglcontextlost, "");
   Host()->HostDispatchEvent(event);
   restore_allowed_ = event->defaultPrevented();
-  if (restore_allowed_ && !is_hidden_) {
-    if (auto_recovery_method_ == kAuto) {
-      // Defer the restore timer to give the context loss
-      // notifications time to propagate through the system: in
-      // particular, to the browser process.
-      restore_timer_.StartOneShot(kDurationBetweenRestoreAttempts, FROM_HERE);
-    }
+  if (restore_allowed_ && auto_recovery_method_ == kAuto) {
+    // Defer the restore timer to give the context loss
+    // notifications time to propagate through the system: in
+    // particular, to the browser process.
+    restore_timer_.StartOneShot(kDurationBetweenRestoreAttempts, FROM_HERE);
   }
 
   if (!restore_allowed_) {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 9d9182e6..ddb31ceb 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -847,7 +847,6 @@
 
   bool is_origin_top_left_ = false;
 
-  bool is_hidden_ = false;
   LostContextMode context_lost_mode_ = kNotLostContext;
   AutoRecoveryMethod auto_recovery_method_ = kManual;
   // Dispatches a context lost event once it is determined that one is needed.
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index df95fe1..06384b8 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -168,8 +168,8 @@
   GPUAdapter* gpu_adapter = nullptr;
   switch (status) {
     case WGPURequestAdapterStatus_Success:
-      gpu_adapter = MakeGarbageCollected<GPUAdapter>(this, "Default", adapter,
-                                                     dawn_control_client_);
+      gpu_adapter =
+          MakeGarbageCollected<GPUAdapter>(this, adapter, dawn_control_client_);
       break;
 
     // Note: requestAdapter never rejects, but we print a console warning if
@@ -216,7 +216,6 @@
 
   IdentifiableTokenBuilder output_builder;
   if (adapter) {
-    output_builder.AddToken(IdentifiabilityBenignStringToken(adapter->name()));
     for (const auto& feature : adapter->features()->FeatureNameSet()) {
       output_builder.AddToken(IdentifiabilityBenignStringToken(feature));
     }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index d2fb2d4..d7a0e04 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -86,13 +86,9 @@
 
 GPUAdapter::GPUAdapter(
     GPU* gpu,
-    const String& name,
     WGPUAdapter handle,
     scoped_refptr<DawnControlClientHolder> dawn_control_client)
-    : DawnObjectBase(dawn_control_client),
-      name_(name),
-      handle_(handle),
-      gpu_(gpu) {
+    : DawnObjectBase(dawn_control_client), handle_(handle), gpu_(gpu) {
   WGPUAdapterProperties properties = {};
   GetProcs().adapterGetProperties(handle_, &properties);
   is_fallback_adapter_ = properties.adapterType == WGPUAdapterType_CPU;
@@ -135,10 +131,6 @@
   }
 }
 
-const String& GPUAdapter::name() const {
-  return name_;
-}
-
 GPUSupportedFeatures* GPUAdapter::features() const {
   return features_;
 }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
index 45bd1e5..27742ace 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
@@ -26,7 +26,6 @@
 
  public:
   GPUAdapter(GPU* gpu,
-             const String& name,
              WGPUAdapter handle,
              scoped_refptr<DawnControlClientHolder> dawn_control_client);
 
@@ -35,7 +34,6 @@
 
   void Trace(Visitor* visitor) const override;
 
-  const String& name() const;
   GPU* gpu() const { return gpu_; }
   GPUSupportedFeatures* features() const;
   GPUSupportedLimits* limits() const { return limits_; }
@@ -64,7 +62,6 @@
                                WGPUDevice dawn_device,
                                const char* error_message);
 
-  String name_;
   WGPUAdapter handle_;
   Member<GPU> gpu_;
   bool is_fallback_adapter_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl b/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl
index dd0eb3ec..ee5fbe5 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl
@@ -8,7 +8,6 @@
     Exposed(Window WebGPU, DedicatedWorker WebGPU),
     SecureContext
 ] interface GPUAdapter {
-    [HighEntropy=Direct, MeasureAs=GPUAdapter_Name] readonly attribute DOMString name;
     [SameObject] readonly attribute GPUSupportedFeatures features;
     [SameObject] readonly attribute GPUSupportedLimits limits;
     readonly attribute boolean isFallbackAdapter;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
index df89b5b..df05d7be 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -99,7 +99,7 @@
   HibernatedCanvasMemoryDumpProvider::GetInstance().Register(this);
 
   // Don't bother compressing very small canvases.
-  if (original_memory_size() < 16 * 1024 ||
+  if (ImageMemorySize(*image) < 16 * 1024 ||
       !base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage)) {
     return;
   }
@@ -187,6 +187,15 @@
   sk_sp<SkData> encoded =
       params->image->encodeToData(SkEncodedImageFormat::kPNG, 100);
 
+  size_t original_memory_size = ImageMemorySize(*params->image);
+  int compression_ratio_percentage = static_cast<int>(
+      (static_cast<size_t>(100) * encoded->size()) / original_memory_size);
+  UMA_HISTOGRAM_PERCENTAGE("Blink.Canvas.2DLayerBridge.Compression.Ratio",
+                           compression_ratio_percentage);
+  UMA_HISTOGRAM_CUSTOM_COUNTS(
+      "Blink.Canvas.2DLayerBridge.Compression.SnapshotSizeKb",
+      static_cast<int>(original_memory_size / 1024), 10, 500000, 50);
+
   auto* reply_task_runner = params->reply_task_runner.get();
   reply_task_runner->PostTask(
       FROM_HERE,
@@ -205,8 +214,15 @@
   DCHECK(
       base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage));
 
+  base::TimeTicks before = base::TimeTicks::Now();
   // Note: not discarding the encoded image.
-  return SkImage::MakeFromEncoded(encoded_)->makeRasterImage();
+  auto image = SkImage::MakeFromEncoded(encoded_)->makeRasterImage();
+  base::TimeTicks after = base::TimeTicks::Now();
+  UMA_HISTOGRAM_TIMES(
+      "Blink.Canvas.2DLayerBridge.Compression.DecompressionTime",
+      after - before);
+  return image;
+  ;
 }
 
 void HibernationHandler::Clear() {
@@ -226,6 +242,12 @@
   }
 }
 
+// static
+size_t HibernationHandler::ImageMemorySize(const SkImage& image) {
+  return static_cast<size_t>(image.height()) * image.width() *
+         image.imageInfo().bytesPerPixel();
+}
+
 size_t HibernationHandler::original_memory_size() const {
   return static_cast<size_t>(width_) * height_ * bytes_per_pixel_;
 }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
index 475e747..d553ae4e5 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -143,6 +143,7 @@
       std::unique_ptr<HibernationHandler::BackgroundTaskParams> params,
       sk_sp<SkData> encoded);
   scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner() const;
+  static size_t ImageMemorySize(const SkImage& image);
 
   // Incremented each time the canvas is hibernated.
   uint64_t epoch_ = 0;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
index b8ece06..7683956 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -35,6 +35,7 @@
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/trace_event/memory_allocator_dump.h"
@@ -1120,6 +1121,7 @@
       {features::kCanvas2DHibernation,
        features::kCanvasCompressHibernatedImage},
       {});
+  base::HistogramTester histogram_tester;
 
   auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>();
   ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
@@ -1152,8 +1154,18 @@
   EXPECT_LT(handler.memory_size(), uncompressed_size);
   EXPECT_EQ(handler.original_memory_size(), uncompressed_size);
 
+  histogram_tester.ExpectTotalCount(
+      "Blink.Canvas.2DLayerBridge.Compression.Ratio", 1);
+  histogram_tester.ExpectUniqueSample(
+      "Blink.Canvas.2DLayerBridge.Compression.SnapshotSizeKb",
+      uncompressed_size / 1024, 1);
+  histogram_tester.ExpectTotalCount(
+      "Blink.Canvas.2DLayerBridge.Compression.DecompressionTime", 0);
+
   SetIsInHiddenPage(bridge.get(), platform, false);
   EXPECT_FALSE(handler.is_encoded());
+  histogram_tester.ExpectTotalCount(
+      "Blink.Canvas.2DLayerBridge.Compression.DecompressionTime", 1);
 
   EXPECT_TRUE(bridge->IsAccelerated());
   EXPECT_FALSE(bridge->IsHibernating());
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 1ccf618..33000a3 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -545,29 +545,6 @@
   }
 }
 
-// Draws a filled rectangle with a stroked border.
-void GraphicsContext::DrawRect(const gfx::Rect& rect,
-                               const AutoDarkMode& auto_dark_mode) {
-  if (rect.IsEmpty())
-    return;
-
-  SkRect sk_rect = gfx::RectToSkRect(rect);
-  if (ImmutableState()->FillColor().Alpha())
-    DrawRect(sk_rect, ImmutableState()->FillFlags(), auto_dark_mode);
-
-  if (ImmutableState()->GetStrokeData().Style() != kNoStroke &&
-      ImmutableState()->StrokeColor().Alpha()) {
-    // Stroke a width: 1 inset border
-    cc::PaintFlags flags(ImmutableState()->FillFlags());
-    flags.setColor(StrokeColor().toSkColor4f());
-    flags.setStyle(cc::PaintFlags::kStroke_Style);
-    flags.setStrokeWidth(1);
-
-    sk_rect.inset(0.5f, 0.5f);
-    DrawRect(sk_rect, flags, auto_dark_mode);
-  }
-}
-
 template <typename TextPaintInfo>
 void GraphicsContext::DrawTextInternal(const Font& font,
                                        const TextPaintInfo& text_info,
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index 7f77d59..67f2ba1ad 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -267,10 +267,6 @@
 
   // ---------- End state management methods -----------------
 
-  // DrawRect() fills and always strokes using a 1-pixel stroke inset from
-  // the rect borders (of the pre-set stroke color).
-  void DrawRect(const gfx::Rect&, const AutoDarkMode& auto_dark_mode);
-
   // DrawLine() only operates on horizontal or vertical lines and uses the
   // current stroke settings. For dotted or dashed stroke, the line need to be
   // top-to-down or left-to-right to get correct interval of dots/dashes.
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
index ea98312e..7aea5ae 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
@@ -60,7 +60,7 @@
                              const gfx::Rect& bounds) {
     return Draw(context, client, type, [&]() {
       DrawingRecorder recorder(context, client, type, bounds);
-      context.DrawRect(bounds, AutoDarkMode::Disabled());
+      context.FillRect(bounds, AutoDarkMode::Disabled());
     });
   }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc b/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc
index f06d6d7f..29ea11dd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc
@@ -20,6 +20,8 @@
 
 namespace blink {
 
+namespace {
+
 Vector<mojom::blink::ServerTimingInfoPtr>
 ParseServerTimingFromHeaderValueToMojo(const String& value) {
   std::unique_ptr<ServerTimingHeaderVector> headers =
@@ -33,6 +35,8 @@
   return result;
 }
 
+}  // namespace
+
 mojom::blink::ResourceTimingInfoPtr CreateResourceTimingInfo(
     base::TimeTicks start_time,
     const KURL& initial_url,
@@ -81,11 +85,11 @@
     }
   }
 
-  bool passes_cors = response->IsCorsSameOrigin();
+  bool allow_response_details = response->IsCorsSameOrigin();
 
   info->content_type = g_empty_string;
 
-  if (passes_cors) {
+  if (allow_response_details) {
     info->response_status = response->HttpStatusCode();
     if (!response->HttpContentType().IsNull()) {
       info->content_type = response->HttpContentType();
@@ -94,7 +98,7 @@
 
   bool expose_body_sizes =
       RuntimeEnabledFeatures::ResourceTimingUseCORSForBodySizesEnabled()
-          ? passes_cors
+          ? allow_response_details
           : info->allow_timing_details;
 
   if (expose_body_sizes && response) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h b/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h
index 51382262..bcae05dc 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h
@@ -17,9 +17,6 @@
 class KURL;
 class ResourceResponse;
 
-BLINK_PLATFORM_EXPORT WTF::Vector<mojom::blink::ServerTimingInfoPtr>
-ParseServerTimingFromHeaderValueToMojo(const String& value);
-
 BLINK_PLATFORM_EXPORT mojom::blink::ResourceTimingInfoPtr
 CreateResourceTimingInfo(base::TimeTicks start_time,
                          const KURL& initial_url,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index ab952dfc..70d9703 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1798,7 +1798,12 @@
       status: "experimental",
       // Because the OT is already underway, it is being left with the "popup" name.
       origin_trial_feature_name: "HTMLPopupAttribute",
-      implied_by: ["HTMLSelectMenuElement"],
+      implied_by: ["HTMLSelectMenuElement","HTMLPopoverHint"],
+    },
+    {
+      // TODO(crbug.com/1416284): Enables popover=hint functionality.
+      name: "HTMLPopoverHint",
+      status: "experimental",
     },
     {
       name: "HTMLSelectMenuElement",
@@ -3093,14 +3098,6 @@
       name: "SpeculationRulesPrefetchWithSubresources",
       base_feature: "none",
     },
-    // Adds a "referrer_policy" key to speculation rules. See https://crbug.com/1355146
-    // TODO(mcnee): This feature shipped in M111. Clean up this flag in M113.
-    {
-      name: "SpeculationRulesReferrerPolicyKey",
-      status: "stable",
-      origin_trial_feature_name: "SpeculationRulesReferrerPolicyKey",
-      implied_by: ["SpeculationRulesPrefetchFuture"],
-    },
     {
       name: "SpeculationRulesRelativeToDocument",
       base_feature: "none",
diff --git a/third_party/blink/renderer/platform/storage/blink_storage_key_fuzzer.cc b/third_party/blink/renderer/platform/storage/blink_storage_key_fuzzer.cc
index 2183b48..9620cea 100644
--- a/third_party/blink/renderer/platform/storage/blink_storage_key_fuzzer.cc
+++ b/third_party/blink/renderer/platform/storage/blink_storage_key_fuzzer.cc
@@ -6,18 +6,19 @@
 #include "base/i18n/icu_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "mojo/core/embedder/embedder.h"
-#include "mojo/public/cpp/bindings/message.h"
 #include "net/base/features.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/common/storage_key/storage_key_mojom_traits.h"
 #include "third_party/blink/public/mojom/storage_key/storage_key.mojom-shared.h"
 #include "third_party/blink/renderer/platform/storage/blink_storage_key.h"
 #include "third_party/blink/renderer/platform/storage/blink_storage_key_mojom_traits.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 
 struct Environment {
   Environment() {
     CHECK(base::i18n::InitializeICU());
     mojo::core::Init();
+    WTF::Partitions::Initialize();
   }
   // used by ICU integration.
   base::AtExitManager at_exit_manager;
@@ -45,15 +46,15 @@
     mojom_serialized_as_wtf.AppendRange(mojom_serialized.begin(),
                                         mojom_serialized.end());
     blink::BlinkStorageKey mojom_blink_storage_key;
-    blink::mojom::blink::StorageKey::Deserialize(mojom_serialized_as_wtf,
-                                                 &mojom_blink_storage_key);
+    assert(blink::mojom::blink::StorageKey::Deserialize(
+        mojom_serialized_as_wtf, &mojom_blink_storage_key));
     WTF::Vector<uint8_t> mojom_blink_serialized =
         blink::mojom::blink::StorageKey::Serialize(&mojom_blink_storage_key);
     std::vector<uint8_t> mojom_blink_serialized_as_std(
         mojom_blink_serialized.begin(), mojom_blink_serialized.end());
     blink::StorageKey mojom_storage_key;
-    blink::mojom::StorageKey::Deserialize(mojom_blink_serialized_as_std,
-                                          &mojom_storage_key);
+    assert(blink::mojom::StorageKey::Deserialize(mojom_blink_serialized_as_std,
+                                                 &mojom_storage_key));
     assert(maybe_storage_key->ExactMatchForTesting(mojom_storage_key));
 
     // Test type conversion path.
diff --git a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
index 48b7ecdb..476f834 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
@@ -144,7 +144,7 @@
                 port.baseline_version_dir(),
                 self._file_name_for_expected_result(virtual_test_name, suffix))
 
-            if port.skips_test(test_name):
+            if port.skips_test(virtual_test_name):
                 self._log_skipped_test(port, virtual_test_name)
                 if self._tool.filesystem.exists(new_baseline):
                     self._tool.filesystem.remove(new_baseline)
diff --git a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines_unittest.py
index 3ebbd27..a0e2f64 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines_unittest.py
@@ -3,9 +3,11 @@
 # found in the LICENSE file.
 
 import optparse
+from unittest import mock
 
 from blinkpy.tool.commands.rebaseline_unittest import BaseTestCase
 from blinkpy.tool.commands.copy_existing_baselines import CopyExistingBaselines
+from blinkpy.web_tests.port.base import VirtualTestSuite
 
 
 class TestCopyExistingBaselines(BaseTestCase):
@@ -297,6 +299,37 @@
                     'platform/test-linux-trusty/failures/expected/image-expected.txt'
                 )))
 
+    def test_no_copy_skipped_virtual_test(self):
+        """Verify that a virtual test skipped for a platform is not copied."""
+        linux_only_suite = VirtualTestSuite(
+            prefix='linux-only',
+            platforms=['Linux'],
+            bases=['failures/expected/image.html'],
+            args=['--dummy-flag'])
+        self._write(
+            self.baseline_path(
+                'platform/test-win-win10/failures/expected/image-expected.txt'
+            ), 'original test-win-win10 result')
+
+        with mock.patch(
+                'blinkpy.web_tests.port.test.TestPort.virtual_test_suites',
+                return_value=[linux_only_suite]):
+            self.command.execute(
+                self.options(port_name='test-win-win10',
+                             test='failures/expected/image.html'), [],
+                self.tool)
+
+        self.assertEqual(
+            self._read(
+                self.baseline_path(
+                    'platform/test-linux-trusty/failures/expected/image-expected.txt'
+                )), 'original test-win-win10 result')
+        self.assertFalse(
+            self.tool.filesystem.exists(
+                self.baseline_path(
+                    'platform/test-win-win10/virtual/linux-only/failures/expected/image-expected.txt'
+                )))
+
     def test_port_for_primary_baseline(self):
         # Testing a protected method - pylint: disable=protected-access
         self.assertEqual(
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
index dd8883d53..ff93640 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
@@ -594,7 +594,6 @@
              stale expectations by default.
            * 'known_intermittent': When omitted, no intermittent statuses are
               expected.
-           * 'duration': Time taken to run the test.
 
         See Also:
             https://github.com/web-platform-tests/wpt/blob/131b8a541ba98afcef35ae757e4fb2f805714230/tools/wptrunner/wptrunner/metadata.py#L439-L450
@@ -603,6 +602,9 @@
         compact_results = []
         for result in results:
             compact_result = {'status': result['status']}
+            duration = result.get('duration')
+            if duration:
+                compact_result['duration'] = duration
             expected = result.get('expected')
             if expected and expected != result['status']:
                 compact_result['expected'] = expected
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
index 148ea78..948744bb 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
@@ -992,5 +992,7 @@
             }],
             'status':
             'OK',
+            'duration':
+            1000,
             'known_intermittent': ['CRASH'],
         }])
diff --git a/third_party/blink/tools/run_wpt_tests.py b/third_party/blink/tools/run_wpt_tests.py
index 470c209..cb51f03e 100755
--- a/third_party/blink/tools/run_wpt_tests.py
+++ b/third_party/blink/tools/run_wpt_tests.py
@@ -927,10 +927,10 @@
                                 'if using only emulators.')
 
             self.provision_devices(devices)
+            self.update_options()
             yield
 
     def update_options(self):
-        super().update_options()
         self._options.device_serial.extend(sorted(self.devices))
         self._options.package_name = (self._options.package_name
                                       or self.get_browser_package_name())
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 79fd021..c3631d8 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2924,6 +2924,8 @@
 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/css-images/image-set/image-set-zero-resolution-rendering-2.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html [ Failure ]
 crbug.com/626703 virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/dedicated-worker.https.window.html [ Failure Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/html/semantics/links/hyperlink-auditing/headers.optional.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] external/wpt/html/semantics/links/hyperlink-auditing/headers.optional.html [ Timeout ]
@@ -6890,16 +6892,16 @@
 crbug.com/v8/13743 fast/forms/ValidityState-patternMismatch.html [ Failure Pass ]
 
 # To land rename CL in devtools repo
-crbug.com/1418045 http/tests/devtools/tracing/buffer-usage.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/report-protocol-errors.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/tracing/trace-event-self-time.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/oopif/oopif-elements-inspect.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/oopif/oopif-performance-monitor.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/tracing/timeline-js/cpu-profile-unsorted-timestamps.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/profiler/sampling-profiler-basic.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/sdk/network-interception-wildcard-pattern-matching.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/elements/event-listeners-framework-with-service-worker.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/service-workers/service-worker-agents.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/service-workers/service-worker-manager.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/persistence/persistence-sync-content-nodejs.js [ Timeout Crash Failure Pass ]
-crbug.com/1418045 http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Timeout Crash Failure Pass ]
+crbug.com/1418045 http/tests/devtools/tracing/buffer-usage.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/report-protocol-errors.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/tracing/trace-event-self-time.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/oopif/oopif-elements-inspect.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/oopif/oopif-performance-monitor.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/tracing/timeline-js/cpu-profile-unsorted-timestamps.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/profiler/sampling-profiler-basic.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/sdk/network-interception-wildcard-pattern-matching.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/elements/event-listeners-framework-with-service-worker.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/service-workers/service-worker-agents.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/service-workers/service-worker-manager.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/persistence/persistence-sync-content-nodejs.js [ Crash Failure Pass Timeout ]
+crbug.com/1418045 http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Crash Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 7194fcf24..4b87e444 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1174,10 +1174,18 @@
     "prefix": "popover-disabled",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [],
-    "args": ["--disable-blink-features=HTMLSelectMenuElement,HTMLPopoverAttribute"],
+    "args": ["--disable-blink-features=HTMLSelectMenuElement,HTMLPopoverAttribute,HTMLPopoverHint"],
     "expires": "Jul 1, 2023"
   },
   {
+    "prefix": "popover-hint-disabled",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": [],
+    "args": ["--enable-blink-features=HTMLPopoverAttribute",
+      "--disable-blink-features=HTMLPopoverHint"],
+    "expires": "Jan 1, 2024"
+  },
+  {
     "prefix": "expose-lcp-render-time",
     "platforms": [
       "Linux",
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 94113dc..b9b1c83 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
@@ -91048,6 +91048,19 @@
        {}
       ]
      ],
+     "out-of-flow-in-multicolumn-116.html": [
+      "fac536a65b34163867de1aad2b7f7378046abb47",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "overflow-clip-000.html": [
       "72b10f5cdd3092a042f1f90bff04e9428d61608e",
       [
@@ -135221,32 +135234,6 @@
         {}
        ]
       ],
-      "image-set-invalid-resolution-rendering-2.html": [
-       "c8b560b7fcb3773947ea6c443441fe4397d3bf8e",
-       [
-        null,
-        [
-         [
-          "/css/css-images/image-set/reference/image-set-rendering-ref.html",
-          "=="
-         ]
-        ],
-        {}
-       ]
-      ],
-      "image-set-invalid-resolution-rendering.html": [
-       "91aeaccebdc323a45240ab971989fe1f6464f46b",
-       [
-        null,
-        [
-         [
-          "/css/css-images/image-set/reference/image-set-rendering-ref.html",
-          "=="
-         ]
-        ],
-        {}
-       ]
-      ],
       "image-set-linear-gradient-rendering.html": [
        "2fc31b95a81e3c46fde0f42edcaad04c1ed6f1b9",
        [
@@ -135519,6 +135506,32 @@
         ],
         {}
        ]
+      ],
+      "image-set-zero-resolution-rendering-2.html": [
+       "5c835a3fbcc8b4304d4bf13ff4bd037d8f518dd7",
+       [
+        null,
+        [
+         [
+          "/css/css-images/image-set/reference/image-set-rendering-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "image-set-zero-resolution-rendering.html": [
+       "d7e4eb7fdc25af16c17e9e7549f67389afea3285",
+       [
+        null,
+        [
+         [
+          "/css/css-images/image-set/reference/image-set-rendering-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "infinite-radial-gradient-refcrash.html": [
@@ -153236,7 +153249,7 @@
       ]
      ],
      "nesting-basic.html": [
-      "dde3ebbcfdb8d268d22fa8ce6940655808c0a202",
+      "19ff48e4a29588e48eb19570149ad7525827cd8c",
       [
        null,
        [
@@ -265222,16 +265235,6 @@
    }
   },
   "support": {
-   ".cache": {
-    "gitignore2.json": [
-     "6e2c795a75ebbba367fa5a46852dca88bca7c835",
-     []
-    ],
-    "mtime.json": [
-     "a5fe875091039492485267682436620cc735681f",
-     []
-    ]
-   },
    ".gitignore": [
     "d93e645d547894b50149d3726de2654957b6e06f",
     []
@@ -269355,6 +269358,10 @@
      "5e812c1ab92b6abc720216be781f821b7b592661",
      []
     ],
+    "compute_pressure_cross_origin_focus_control.tentative.https.window.js.ini": [
+     "ec03acb4589e5e5a957eb619eed700367900d6bb",
+     []
+    ],
     "compute_pressure_detached_iframe.tentative.https.html.ini": [
      "35432640a4335b60d7e1e71b28aa9a19b58f61da",
      []
@@ -269387,6 +269394,10 @@
      "5970aeaff5128f90add28045f94e294f211aab73",
      []
     ],
+    "compute_pressure_same_origin_focus_control.tentative.https.window.js.ini": [
+     "dc7f8d7cb367d3d19a0f150169f676a8c0d46407",
+     []
+    ],
     "compute_pressure_take_records.tentative.https.window.js.ini": [
      "94e875de071bb838887e77a0bba13b6c0cdbf2b5",
      []
@@ -300249,6 +300260,14 @@
        "f2b544a7d84384c975f58ab57a829a3c4cee6556",
        []
       ],
+      "image-set-zero-resolution-rendering-2.html.ini": [
+       "778000386d8fe4d83aff87cb8cf8a4bdb938e6fc",
+       []
+      ],
+      "image-set-zero-resolution-rendering.html.ini": [
+       "f2243377a7f43e8907262abf66cbf70811e578b9",
+       []
+      ],
       "reference": {
        "image-set-conic-gradient-rendering-ref.html": [
         "2a5133ee94e792877410cae71be904c585bf0a6c",
@@ -304210,6 +304229,10 @@
       "7c33b48f5e6077ad87a230d8ca54976e55785445",
       []
      ],
+     "getclientrects-000.html.ini": [
+      "23360a44c7cd81c0d6e465d0401c5ec851b792e6",
+      []
+     ],
      "getclientrects-001.html.ini": [
       "59242d8056b3a4cf5812991a1f2e79d1238eac22",
       []
@@ -305295,7 +305318,7 @@
       []
      ],
      "nesting-basic-ref.html": [
-      "8825ad47d277feb6e64fde381107833e84ccbc56",
+      "8038f369cde2b496106223551ab9441a79d2621e",
       []
      ],
      "supports-is-consistent-ref.html": [
@@ -309366,6 +309389,10 @@
          []
         ]
        },
+       "shape-image-003.html.ini": [
+        "0a4d469255313aec5fabc656327057b20c1c15fe",
+        []
+       ],
        "shape-image-004.html.ini": [
         "461a202b5972e2da219fa1d18784033872891be9",
         []
@@ -315049,6 +315076,10 @@
        "be8ba325fbe07e9c6a2af6f40cdeca78f4c9c841",
        []
       ],
+      "text-transform-capitalize-022.html.ini": [
+       "f4a6665417c2b26b7fccca3fd7b5e05e219e0e30",
+       []
+      ],
       "text-transform-capitalize-026.html.ini": [
        "6e52a555638556b0cd1cc16bf167482196479d88",
        []
@@ -320813,6 +320844,10 @@
        "2a170629651d9defa1951ceceeab784ca50ea285",
        []
       ],
+      "kind-of-widget-fallback-input-submit-border-right-width-001.html.ini": [
+       "1e55e271c6c6f134ac658bcefa5369580ea553da",
+       []
+      ],
       "kind-of-widget-fallback-input-submit-border-top-left-radius-001.html.ini": [
        "3c0a4536d792ebcd543ab708a30b850998a25a60",
        []
@@ -320821,6 +320856,10 @@
        "268cb3d48ba138d9f23501af4e028ac13e099d09",
        []
       ],
+      "kind-of-widget-fallback-input-text-background-origin-001.html.ini": [
+       "b5b8f0ea2c69a7494b89e5b208bfcc303baf20a1",
+       []
+      ],
       "kind-of-widget-fallback-input-text-border-bottom-right-radius-001.html.ini": [
        "8cf94321dc1df346f82158550e4cd2ad2b5b6e3d",
        []
@@ -321036,6 +321075,10 @@
       "kind-of-widget-fallback-textarea-border-start-start-radius-001.html.ini": [
        "7507d0dd65818071cdfc2fdbe3a96d7249cc1e24",
        []
+      ],
+      "kind-of-widget-fallback-textarea-border-top-color-001.html.ini": [
+       "9af42e769df5911125cc0aca08424b5836c96aca",
+       []
       ]
      },
      "compute-kind-widget-no-fallback-props-001.html.ini": [
@@ -328756,10 +328799,6 @@
       "cab488fce6ad05f23f3286d9f53c4d050ee96aa7",
       []
      ],
-     "media-query-matches-in-iframe-expected.txt": [
-      "13cf904ee5ad53b1974139db08974c3a5d8604e8",
-      []
-     ],
      "media-query-matches-in-iframe.html.ini": [
       "e0829db3d9282a76cc3fd2039bfbf968080f3a6f",
       []
@@ -328838,12 +328877,8 @@
        []
       ]
      },
-     "test_media_queries-expected.txt": [
-      "0baac7d12c7ddbfb6e3bdf86ea5ddecc8447ab46",
-      []
-     ],
      "test_media_queries.html.ini": [
-      "b0ddcef7ba1396027ff2a7c18331b6f0f222857a",
+      "f21b93fe675e818d92f8a5aaabe0ca101e31e3eb",
       []
      ],
      "viewport-script-dynamic-ref.html": [
@@ -343294,10 +343329,18 @@
         "c6e046747c6092994e7f72c3132299b3dada8978",
         []
        ],
+       "open-features-non-integer-height.html.ini": [
+        "2c26934862f4a84b273590a8b7cf185ae15c1940",
+        []
+       ],
        "open-features-non-integer-innerheight.html.ini": [
         "aadc15b7492590b3a6bc7106f2fed3a5000777a7",
         []
        ],
+       "open-features-non-integer-innerwidth.html.ini": [
+        "e28fcf4bfc56438a02a9283766541df2910677b7",
+        []
+       ],
        "open-features-non-integer-left-expected.txt": [
         "62b1506da1c9b363345ccfdd989590ac6ec30d5c",
         []
@@ -346716,6 +346759,10 @@
       "46ad58d83bf6e98913ca4c564b7acb8f19fa0093",
       []
      ],
+     "popup-meta-http-equiv.https.html.ini": [
+      "bb5e9fa51915d02574ebdba9c23f557f5e567360",
+      []
+     ],
      "popup-redirect-cache.https.html.headers": [
       "46ad58d83bf6e98913ca4c564b7acb8f19fa0093",
       []
@@ -355239,7 +355286,7 @@
         []
        ],
        "select-event.html.ini": [
-        "4cabb085251cfd1eac6bc694eb7c963be6d1ab30",
+        "7ac279e52f4eacc5275677ac1604146157a45e77",
         []
        ]
       },
@@ -362526,7 +362573,7 @@
      []
     ],
     "cross-origin-iframe.sub.html.ini": [
-     "87d0f1d05a85f0703a13ce619a3e7ad54bf3d91f",
+     "49c5d9b0639187d7cd2e5e53309c1984628f6c86",
      []
     ],
     "idlharness.window-expected.txt": [
@@ -362594,7 +362641,13 @@
     "same-origin-grand-child-iframe.sub.html.ini": [
      "8cc9dfe7b58ef24eaea9603008ad524a6f3551c2",
      []
-    ]
+    ],
+    "v2": {
+     "delay-test.html.ini": [
+      "9900ba92af3389fbee98bd06a35b2df172f3527e",
+      []
+     ]
+    }
    },
    "intervention-reporting": {
     "META.yml": [
@@ -371943,7 +371996,7 @@
       []
      ],
      "frame-timing.js": [
-      "019bd424b55065451eb4ad0a132d3a6befbbb5fc",
+      "65710e22c8fc39b4cc3ef5aa93460a2f4736f8d6",
       []
      ],
      "frameset-timing-frame.html": [
@@ -372070,6 +372123,14 @@
       "c104f3c8f0695a96646cf24a730eef0342f95876",
       []
      ],
+     "object-frame-options-200.asis": [
+      "122445326bdef6d058e1adf4d6a2b51e4f6889da",
+      []
+     ],
+     "object-frame-options-403.asis": [
+      "fd64f0bcdbb7c17f60f485a25cfa78c6317e0ce6",
+      []
+     ],
      "object-navigate-back.html": [
       "a746947818ffb4e36367f620d603c2f6874dab10",
       []
@@ -416356,6 +416417,25 @@
       }
      ]
     ],
+    "compute_pressure_cross_origin_focus_control.tentative.https.window.js": [
+     "982e4bba0e6cb70d5eef7361a0110ab4b07be20e",
+     [
+      "compute-pressure/compute_pressure_cross_origin_focus_control.tentative.https.window.html",
+      {
+       "script_metadata": [
+        [
+         "timeout",
+         "long"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ]
+    ],
     "compute_pressure_detached_iframe.tentative.https.html": [
      "3b0a5504e536acf9426476122f59b7c8ed24b150",
      [
@@ -416456,7 +416536,7 @@
      ]
     ],
     "compute_pressure_privacy_test.tentative.https.window.js": [
-     "3a4198f547cc4b5f5fdf075f13efabc8429408a5",
+     "ceea886323cdfcb37f3deda425d90ef358a28c59",
      [
       "compute-pressure/compute_pressure_privacy_test.tentative.https.window.html",
       {
@@ -416494,6 +416574,13 @@
       }
      ]
     ],
+    "compute_pressure_same_origin_focus_control.tentative.https.window.js": [
+     "746f119f249c8c1a7d6451a5672823d011adf22e",
+     [
+      "compute-pressure/compute_pressure_same_origin_focus_control.tentative.https.window.html",
+      {}
+     ]
+    ],
     "compute_pressure_supported_sources.tentative.https.window.js": [
      "2a69e731e7f9f7c5c0b54ea3b7bc9202bda57f16",
      [
@@ -451527,7 +451614,7 @@
       },
       "inline": {
        "append.tentative.html": [
-        "ee9a9e4ddbcf78a7517d8d038d66844880719e63",
+        "f80875622366939f48a7471513fb6319f75be718",
         [
          null,
          {}
@@ -458373,6 +458460,13 @@
        null,
        {}
       ]
+     ],
+     "update-media-feature.html": [
+      "8064117da73f956625f309263c9401b8adafc3a3",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "motion": {
@@ -582685,6 +582779,15 @@
       {}
      ]
     ],
+    "entries-for-object-frame-options-deny.html": [
+     "a69b642971ba6adfbde8afcd451b29603cb7fcf3",
+     [
+      null,
+      {
+       "timeout": "long"
+      }
+     ]
+    ],
     "entry-attributes.html": [
      "94f219f229107e4764134187f111dcc7757f617d",
      [
@@ -582782,7 +582885,7 @@
      ]
     ],
     "iframe-sequence-of-events.html": [
-     "02d1c362c9df491439fd81ed9be48d5859a2e94f",
+     "5d719fb9153f18f35d239c3135f3fd844aa8558c",
      [
       null,
       {}
diff --git a/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_cross_origin_focus_control.tentative.https.window.js.ini b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_cross_origin_focus_control.tentative.https.window.js.ini
new file mode 100644
index 0000000..ec03acb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_cross_origin_focus_control.tentative.https.window.js.ini
@@ -0,0 +1,2 @@
+[compute_pressure_cross_origin_focus_control.tentative.https.window.html]
+  expected: CRASH
diff --git a/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_same_origin_focus_control.tentative.https.window.js.ini b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_same_origin_focus_control.tentative.https.window.js.ini
new file mode 100644
index 0000000..dc7f8d7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compute-pressure/compute_pressure_same_origin_focus_control.tentative.https.window.js.ini
@@ -0,0 +1,2 @@
+[compute_pressure_same_origin_focus_control.tentative.https.window.html]
+  expected: CRASH
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-116.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-116.html
new file mode 100644
index 0000000..fac536a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-116.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1412313">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:1; column-fill:auto; height:100px; width:100px;">
+  <div style="contain:size; height:100px; background:green;">
+    <div style="height:50px;"></div>
+    <span style="position:relative; height:50px;">
+      <div style="height:25px; background:red;"></div>
+      <div style="position:absolute; top:0; width:100px; height:25px; background:green;"></div>
+    </span>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/background-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/background-001.tentative.html
new file mode 100644
index 0000000..2084f4ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/background-001.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1418818">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-fill:auto; gap:0; width:100px; height:400px;">
+  <div style="display:table; width:100%;">
+    <div style="display:table-header-group; break-inside:avoid;">
+      <div style="height:100px; background:linear-gradient(green 100px, red 100px);"></div>
+    </div>
+    <div style="height:400px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-invalid-resolution-rendering-2.html b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering-2.html
similarity index 72%
rename from third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-invalid-resolution-rendering-2.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering-2.html
index c8b560b7..5c835a3f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-invalid-resolution-rendering-2.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering-2.html
@@ -4,13 +4,13 @@
 <link rel="author" title="Traian Captan" href="mailto:tcaptan@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
 <link rel="match" href="reference/image-set-rendering-ref.html">
-<meta name="assert" content="image-set rendering when resolution is invalid">
+<meta name="assert" content="image-set rendering with zero resolution">
 <style>
   #test {
-    background-image: url("/images/green.png");
+    background-image: url("/images/red.png");
     background-image: image-set(
-      url("/images/red.png") 0x,
-      url("/images/red.png") 2x
+      url("/images/green.png") 0x,
+      url("/images/green.png") 2x
     );
     width: 100px;
     height: 100px;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering-2.html.ini b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering-2.html.ini
new file mode 100644
index 0000000..7780003
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering-2.html.ini
@@ -0,0 +1,2 @@
+[image-set-zero-resolution-rendering-2.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-invalid-resolution-rendering.html b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html
similarity index 71%
rename from third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-invalid-resolution-rendering.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html
index 91aeacc..d7e4eb7f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-invalid-resolution-rendering.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html
@@ -4,11 +4,11 @@
 <link rel="author" title="Traian Captan" href="mailto:tcaptan@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
 <link rel="match" href="reference/image-set-rendering-ref.html">
-<meta name="assert" content="image-set rendering when resolution is invalid">
+<meta name="assert" content="image-set rendering with zero resolution">
 <style>
   #test {
-    background-image: url("/images/green.png");
-    background-image: image-set(url("/images/red.png") 0x);
+    background-image: url("/images/red.png");
+    background-image: image-set(url("/images/green.png") 0x);
     width: 100px;
     height: 100px;
   }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html.ini b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html.ini
new file mode 100644
index 0000000..f2243377
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-zero-resolution-rendering.html.ini
@@ -0,0 +1,2 @@
+[image-set-zero-resolution-rendering.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/getclientrects-000.html.ini b/third_party/blink/web_tests/external/wpt/css/css-multicol/getclientrects-000.html.ini
new file mode 100644
index 0000000..23360a44
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/getclientrects-000.html.ini
@@ -0,0 +1,18 @@
+[getclientrects-000.html]
+  [horizontal-tb ltr]
+    expected: [FAIL, PASS]
+
+  [horizontal-tb rtl]
+    expected: [FAIL, PASS]
+
+  [vertical-lr ltr]
+    expected: [FAIL, PASS]
+
+  [vertical-lr rtl]
+    expected: [FAIL, PASS]
+
+  [vertical-rl ltr]
+    expected: [FAIL, PASS]
+
+  [vertical-rl rtl]
+    expected: [FAIL, PASS]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic-ref.html b/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic-ref.html
index 8825ad47d..8038f36 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic-ref.html
@@ -5,8 +5,8 @@
 <style>
   .test {
     background-color: green;
-    width: 100px;
-    height: 100px;
+    width: 30px;
+    height: 30px;
     display: grid;
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic.html b/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic.html
index dde3ebb..19ff48e4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-nesting/nesting-basic.html
@@ -6,8 +6,8 @@
 <style>
   .test {
     background-color: red;
-    width: 100px;
-    height: 100px;
+    width: 30px;
+    height: 30px;
     display: grid;
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini b/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini
new file mode 100644
index 0000000..0a4d469
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini
@@ -0,0 +1,2 @@
+[shape-image-003.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-capitalize-022.html.ini b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-capitalize-022.html.ini
new file mode 100644
index 0000000..f4a6665
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-capitalize-022.html.ini
@@ -0,0 +1,2 @@
+[text-transform-capitalize-022.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-submit-border-right-width-001.html.ini b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-submit-border-right-width-001.html.ini
new file mode 100644
index 0000000..1e55e27
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-submit-border-right-width-001.html.ini
@@ -0,0 +1,2 @@
+[kind-of-widget-fallback-input-submit-border-right-width-001.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-text-background-origin-001.html.ini b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-text-background-origin-001.html.ini
new file mode 100644
index 0000000..b5b8f0e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-text-background-origin-001.html.ini
@@ -0,0 +1,2 @@
+[kind-of-widget-fallback-input-text-background-origin-001.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-textarea-border-top-color-001.html.ini b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-textarea-border-top-color-001.html.ini
new file mode 100644
index 0000000..9af42e76
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-textarea-border-top-color-001.html.ini
@@ -0,0 +1,2 @@
+[kind-of-widget-fallback-textarea-border-top-color-001.html]
+  expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries.html.ini b/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries.html.ini
new file mode 100644
index 0000000..f21b93f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries.html.ini
@@ -0,0 +1,6 @@
+[test_media_queries.html]
+  ['or' keyword: should_not_apply: (height) or (height)]
+    expected: FAIL
+
+  [nesting: should_not_apply: ((height))]
+    expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-height.html.ini b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-height.html.ini
new file mode 100644
index 0000000..2c26934
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-height.html.ini
@@ -0,0 +1,2 @@
+[open-features-non-integer-height.html]
+  expected: ERROR
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html.ini b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html.ini
new file mode 100644
index 0000000..e28fcf4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html.ini
@@ -0,0 +1,2 @@
+[open-features-non-integer-innerwidth.html]
+  expected: [ERROR, OK]
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html.ini b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html.ini
new file mode 100644
index 0000000..bb5e9fa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html.ini
@@ -0,0 +1,2 @@
+[popup-meta-http-equiv.https.html]
+  expected: ERROR
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/textfieldselection/select-event.html.ini b/third_party/blink/web_tests/external/wpt/html/semantics/forms/textfieldselection/select-event.html.ini
index 4cabb08..7ac279e 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/textfieldselection/select-event.html.ini
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/textfieldselection/select-event.html.ini
@@ -4,15 +4,14 @@
 
   [input type password: select() event queue]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [FAIL, PASS]
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       FAIL
 
   [input type password: select() twice in disconnected node (must fire select only once)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "mac"): FAIL
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type password: selectionDirection a second time (must not fire select)]
@@ -39,9 +38,7 @@
       if product == "chrome": FAIL
 
   [input type password: selectionEnd out of range a second time (must not fire select)]
-    expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
-      FAIL
+    expected: FAIL
 
   [input type password: selectionEnd out of range disconnected node]
     expected: FAIL
@@ -57,9 +54,7 @@
     expected: FAIL
 
   [input type password: selectionStart a second time (must not fire select)]
-    expected:
-      if (product == "content_shell") and (os == "mac"): PASS
-      FAIL
+    expected: FAIL
 
   [input type password: selectionStart disconnected node]
     expected: FAIL
@@ -85,17 +80,22 @@
     expected: FAIL
 
   [input type password: setRangeText() a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
+      FAIL
 
   [input type password: setRangeText() disconnected node]
     expected: FAIL
 
   [input type password: setRangeText() event queue]
     expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type password: setRangeText() twice in disconnected node (must fire select only once)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
+      FAIL
 
   [input type password: setSelectionRange out of range a second time (must not fire select)]
     expected: FAIL
@@ -127,39 +127,42 @@
     expected: FAIL
 
   [input type search: select() event queue]
-    expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [FAIL, PASS]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
-      FAIL
+    expected: FAIL
 
   [input type search: select() twice in disconnected node (must fire select only once)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "mac"): FAIL
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [input type search: selectionDirection a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type search: selectionDirection disconnected node]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type search: selectionDirection event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [input type search: selectionDirection twice in disconnected node (must fire select only once)]
     expected: FAIL
 
   [input type search: selectionEnd a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type search: selectionEnd disconnected node]
     expected: FAIL
 
   [input type search: selectionEnd event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [input type search: selectionEnd out of range a second time (must not fire select)]
@@ -180,6 +183,7 @@
 
   [input type search: selectionStart a second time (must not fire select)]
     expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": PASS
       FAIL
 
@@ -188,6 +192,7 @@
 
   [input type search: selectionStart event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type search: selectionStart out of range a second time (must not fire select)]
@@ -207,9 +212,7 @@
     expected: FAIL
 
   [input type search: setRangeText() a second time (must not fire select)]
-    expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
-      FAIL
+    expected: FAIL
 
   [input type search: setRangeText() disconnected node]
     expected: FAIL
@@ -252,40 +255,49 @@
 
   [input type tel: select() event queue]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [FAIL, PASS]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       FAIL
 
   [input type tel: select() twice in disconnected node (must fire select only once)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "mac"): FAIL
       if product == "chrome": FAIL
 
   [input type tel: selectionDirection a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       FAIL
 
   [input type tel: selectionDirection disconnected node]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type tel: selectionDirection event queue]
     expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type tel: selectionDirection twice in disconnected node (must fire select only once)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
+      FAIL
 
   [input type tel: selectionEnd a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type tel: selectionEnd disconnected node]
     expected: FAIL
 
   [input type tel: selectionEnd event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type tel: selectionEnd out of range a second time (must not fire select)]
@@ -305,13 +317,16 @@
     expected: FAIL
 
   [input type tel: selectionStart a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type tel: selectionStart disconnected node]
     expected: FAIL
 
   [input type tel: selectionStart event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type tel: selectionStart out of range a second time (must not fire select)]
@@ -338,13 +353,10 @@
 
   [input type tel: setRangeText() event queue]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type tel: setRangeText() twice in disconnected node (must fire select only once)]
-    expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [FAIL, PASS]
-      FAIL
+    expected: FAIL
 
   [input type tel: setSelectionRange out of range a second time (must not fire select)]
     expected: FAIL
@@ -361,7 +373,7 @@
 
   [input type tel: setSelectionRange() a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       FAIL
 
   [input type tel: setSelectionRange() disconnected node]
@@ -369,38 +381,42 @@
 
   [input type tel: setSelectionRange() event queue]
     expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type tel: setSelectionRange() twice in disconnected node (must fire select only once)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
+      FAIL
 
   [input type text: select() disconnected node]
     expected: FAIL
 
   [input type text: select() event queue]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       FAIL
 
   [input type text: select() twice in disconnected node (must fire select only once)]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
       if (product == "content_shell") and (os == "mac"): FAIL
       if product == "chrome": FAIL
 
   [input type text: selectionDirection a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       FAIL
 
   [input type text: selectionDirection disconnected node]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type text: selectionDirection event queue]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type text: selectionDirection twice in disconnected node (must fire select only once)]
@@ -408,8 +424,8 @@
 
   [input type text: selectionEnd a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
+      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       FAIL
 
   [input type text: selectionEnd disconnected node]
@@ -417,7 +433,7 @@
 
   [input type text: selectionEnd event queue]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type text: selectionEnd out of range a second time (must not fire select)]
@@ -438,8 +454,7 @@
 
   [input type text: selectionStart a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
       if (product == "content_shell") and (os == "mac"): PASS
       FAIL
 
@@ -448,7 +463,7 @@
 
   [input type text: selectionStart event queue]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): FAIL
       if product == "chrome": FAIL
 
   [input type text: selectionStart out of range a second time (must not fire select)]
@@ -465,7 +480,9 @@
     expected: FAIL
 
   [input type text: selectionStart twice in disconnected node (must fire select only once)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
+      FAIL
 
   [input type text: setRangeText() a second time (must not fire select)]
     expected: FAIL
@@ -495,8 +512,7 @@
 
   [input type text: setSelectionRange() a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
+      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
       FAIL
 
   [input type text: setSelectionRange() disconnected node]
@@ -514,38 +530,44 @@
 
   [input type url: select() event queue]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [FAIL, PASS]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       FAIL
 
   [input type url: select() twice in disconnected node (must fire select only once)]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "mac"): FAIL
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [input type url: selectionDirection a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type url: selectionDirection disconnected node]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type url: selectionDirection event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [input type url: selectionDirection twice in disconnected node (must fire select only once)]
     expected: FAIL
 
   [input type url: selectionEnd a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type url: selectionEnd disconnected node]
     expected: FAIL
 
   [input type url: selectionEnd event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type url: selectionEnd out of range a second time (must not fire select)]
@@ -565,13 +587,16 @@
     expected: FAIL
 
   [input type url: selectionStart a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
+      FAIL
 
   [input type url: selectionStart disconnected node]
     expected: FAIL
 
   [input type url: selectionStart event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [input type url: selectionStart out of range a second time (must not fire select)]
@@ -617,26 +642,31 @@
     expected: FAIL
 
   [input type url: setSelectionRange() a second time (must not fire select)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
+      FAIL
 
   [input type url: setSelectionRange() disconnected node]
     expected: FAIL
 
   [input type url: setSelectionRange() event queue]
     expected:
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": FAIL
 
   [input type url: setSelectionRange() twice in disconnected node (must fire select only once)]
-    expected: FAIL
+    expected:
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
+      FAIL
 
   [textarea: select() disconnected node]
     expected:
-      if (product == "content_shell") and (os == "mac"): FAIL
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [textarea: select() event queue]
     expected:
-      if (product == "content_shell") and (os == "mac"): PASS
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       if product == "chrome": PASS
       FAIL
 
@@ -684,7 +714,7 @@
 
   [textarea: selectionStart a second time (must not fire select)]
     expected:
-      if (product == "content_shell") and (os == "mac"): FAIL
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [textarea: selectionStart disconnected node]
@@ -692,8 +722,7 @@
 
   [textarea: selectionStart event queue]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [FAIL, PASS]
-      if (product == "content_shell") and (os == "mac"): PASS
+      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
       FAIL
 
   [textarea: selectionStart out of range a second time (must not fire select)]
@@ -711,8 +740,7 @@
 
   [textarea: selectionStart twice in disconnected node (must fire select only once)]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [PASS, FAIL]
-      if (product == "content_shell") and (os == "mac"): FAIL
+      if (product == "content_shell") and (os == "mac"): [FAIL, PASS]
       if product == "chrome": FAIL
 
   [textarea: setRangeText() a second time (must not fire select)]
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html
index e9167aae..c80534af 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-attribute-basic.html
@@ -15,6 +15,7 @@
   <div popover id=boolean>Pop up</div>
   <div popover="">Pop up</div>
   <div popover=auto>Pop up</div>
+  <div popover=hint>Pop up</div>
   <div popover=manual>Pop up</div>
   <article popover>Different element type</article>
   <header popover>Different element type</header>
@@ -163,6 +164,10 @@
     assert_equals(popover.popover,'manual','Invalid values should reflect as "manual"');
     popover.removeAttribute('popover');
     assert_equals(popover.popover,null,'No value should reflect as null');
+    if (popoverHintSupported()) {
+      popover.popover='hint';
+      assert_equals(popover.getAttribute('popover'),'hint');
+    }
     popover.popover='auto';
     assert_equals(popover.getAttribute('popover'),'auto');
     popover.popover='';
@@ -223,7 +228,13 @@
     const popover = createPopover(t);
     popover.showPopover();
     assert_true(popover.matches(':open'));
-    popover.setAttribute('popover','manual'); // Change popover type
+    if (popoverHintSupported()) {
+      popover.setAttribute('popover','hint'); // Change popover type
+      assert_false(popover.matches(':open'));
+      popover.showPopover();
+      assert_true(popover.matches(':open'));
+      popover.setAttribute('popover','manual');
+    }
     assert_false(popover.matches(':open'));
     popover.showPopover();
     assert_true(popover.matches(':open'));
@@ -237,7 +248,7 @@
     assert_false(popover.matches(':open'),'From "auto" to "invalid" (which is interpreted as "manual") should close the popover');
   },'Changing attribute values should close open popovers');
 
-  const validTypes = ["auto","manual"];
+  const validTypes = popoverHintSupported() ? ["auto","hint","manual"] : ["auto","manual"];
   validTypes.forEach(type => {
     test((t) => {
       const popover = createPopover(t);
@@ -376,6 +387,7 @@
                     popover.hidePopover();
                     break;
                   case 'auto':
+                  case 'hint':
                     assert_false(popover.matches(':open'),'A popover=auto should light-dismiss');
                     break;
                 }
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-backdrop-appearance.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-backdrop-appearance.html
index c71e148..cf57aee 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-backdrop-appearance.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-backdrop-appearance.html
@@ -36,7 +36,7 @@
 <p>Test for [popover]::backdrop presence and stacking order. The test passes
   if there are 3 stacked boxes, with the brightest green on top.</p>
 <div popover id=bottom>Bottom
-  <div popover id=middle>Middle
+  <div popover=hint id=middle>Middle
     <div popover=manual id=top>Top</div>
   </div>
 </div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-invoking-attribute.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-invoking-attribute.html
index 7b1dc20a..9b7a714f 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-invoking-attribute.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-invoking-attribute.html
@@ -10,6 +10,7 @@
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-actions.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
 
 <body>
 <script>
@@ -60,8 +61,9 @@
   ...supportedInputButtonTypes,
   ...unsupportedTypes,
 ];
+const validTypes = popoverHintSupported() ? ["auto","hint","manual"] : ["auto","manual"];
 window.addEventListener('load', () => {
-  ["auto","manual"].forEach(type => {
+  validTypes.forEach(type => {
     invokers.forEach(testcase => {
       ["toggle","hide","show","ShOw","garbage",null,undefined].forEach(action => {
         [false,true].forEach(use_idl_for_target => {
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss.html
index 2e9fdb4..2f7e2de9 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss.html
@@ -461,6 +461,52 @@
 },'Ensure circular/convoluted ancestral relationships are functional, with a direct showPopover()');
 </script>
 
+<div popover id=p10>Popover</div>
+<div popover=hint id=p11>Hint</div>
+<div popover=manual id=p12>Manual</div>
+<style>
+  #p10 {top:100px;}
+  #p11 {top:200px;}
+  #p12 {top:300px;}
+</style>
+<script>
+if (popoverHintSupported()) {
+  promise_test(async () => {
+    const auto = document.querySelector('#p10');
+    const hint = document.querySelector('#p11');
+    const manual = document.querySelector('#p12');
+    // All three can be open at once, if shown in this order:
+    auto.showPopover();
+    hint.showPopover();
+    manual.showPopover();
+    assert_true(auto.matches(':open'));
+    assert_true(hint.matches(':open'));
+    assert_true(manual.matches(':open'));
+    // Clicking the hint will close the auto, but not the manual.
+    await clickOn(hint);
+    assert_false(auto.matches(':open'),'auto should be hidden');
+    assert_true(hint.matches(':open'),'hint should stay open');
+    assert_true(manual.matches(':open'),'manual does not light dismiss');
+    // Clicking outside should close the hint, but not the manual:
+    await clickOn(outside);
+    assert_false(auto.matches(':open'));
+    assert_false(hint.matches(':open'),'hint should close');
+    assert_true(manual.matches(':open'),'manual does not light dismiss');
+    manual.hidePopover();
+    assert_false(manual.matches(':open'));
+    auto.showPopover();
+    hint.showPopover();
+    assert_true(auto.matches(':open'));
+    assert_true(hint.matches(':open'));
+    // Clicking on the auto should close the hint:
+    await clickOn(auto);
+    assert_true(auto.matches(':open'),'auto should stay open');
+    assert_false(hint.matches(':open'),'hint should light dismiss');
+    auto.hidePopover();
+    assert_false(auto.matches(':open'));
+  },'Light dismiss of mixed popover types including hints');
+}
+</script>
 <div popover id=p13>Popover 1
   <div popover id=p14>Popover 2
     <div popover id=p15>Popover 3</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types-with-hints.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types-with-hints.tentative.html
new file mode 100644
index 0000000..39d24a0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types-with-hints.tentative.html
@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popup.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>
+  <div popover>Popover</div>
+  <div popover=hint>Hint</div>
+  <div popover=manual>Async</div>
+  <div popover=manual>Async</div>
+  <script>
+  {
+    const auto = document.currentScript.parentElement.querySelector('[popover=""]');
+    const hint = document.currentScript.parentElement.querySelector('[popover=hint]');
+    const manual = document.currentScript.parentElement.querySelectorAll('[popover=manual]')[0];
+    const manual2 = document.currentScript.parentElement.querySelectorAll('[popover=manual]')[1];
+    function assert_state_1(autoOpen,hintOpen,manualOpen,manual2Open) {
+      assert_equals(auto.matches(':open'),autoOpen,'auto open state is incorrect');
+      assert_equals(hint.matches(':open'),hintOpen,'hint open state is incorrect');
+      assert_equals(manual.matches(':open'),manualOpen,'manual open state is incorrect');
+      assert_equals(manual2.matches(':open'),manual2Open,'manual2 open state is incorrect');
+    }
+    test(() => {
+      assert_state_1(false,false,false,false);
+      auto.showPopover();
+      assert_state_1(true,false,false,false);
+      hint.showPopover();
+      assert_state_1(true,true,false,false);
+      manual.showPopover();
+      assert_state_1(true,true,true,false);
+      manual2.showPopover();
+      assert_state_1(true,true,true,true);
+      hint.hidePopover();
+      assert_state_1(true,false,true,true);
+      auto.hidePopover();
+      assert_state_1(false,false,true,true);
+      auto.showPopover();
+      hint.showPopover();
+      assert_state_1(true,true,true,true);
+      auto.hidePopover(); // Non-nested tooltips can stay open when unrelated popovers are hidden.
+      assert_state_1(false,true,true,true);
+      hint.hidePopover();
+      manual.hidePopover();
+      assert_state_1(false,false,false,true);
+      manual2.hidePopover();
+      assert_state_1(false,false,false,false);
+    },'manuals do not close popovers');
+
+    test(() => {
+      assert_state_1(false,false,false,false);
+      hint.showPopover();
+      manual.showPopover();
+      manual2.showPopover();
+      assert_state_1(false,true,true,true);
+      auto.showPopover();
+      assert_state_1(true,false,true,true);
+      auto.hidePopover();
+      assert_state_1(false,false,true,true);
+      manual.hidePopover();
+      manual2.hidePopover();
+      assert_state_1(false,false,false,false);
+    },'autos close hints but not manuals');
+  }
+  </script>
+</div>
+
+<div>
+  <div popover>popover 1
+    <div popover>popover 2
+      <p id=anchorid>Anchor</p>
+      <div popover>popover 3</div>
+    </div>
+  </div>
+  <div popover=hint anchor=anchorid>Hint anchored to pop-up</div>
+  <script>
+  {
+    const popover1 = document.currentScript.parentElement.querySelectorAll('[popover=""]')[0];
+    const popover2 = document.currentScript.parentElement.querySelectorAll('[popover=""]')[1];
+    const popover3 = document.currentScript.parentElement.querySelectorAll('[popover=""]')[2];
+    const hint = document.currentScript.parentElement.querySelector('[popover=hint]');
+    function assert_state_2(popover1Open,popover2Open,popover3Open,hintOpen) {
+      assert_equals(popover1.matches(':open'),popover1Open,'popover1 open state is incorrect');
+      assert_equals(popover2.matches(':open'),popover2Open,'popover2 open state is incorrect');
+      assert_equals(popover3.matches(':open'),popover3Open,'popover3 open state is incorrect');
+      assert_equals(hint.matches(':open'),hintOpen,'hint open state is incorrect');
+    }
+    test(() => {
+      assert_state_2(false,false,false,false);
+      popover1.showPopover();
+      popover2.showPopover();
+      popover3.showPopover();
+      assert_state_2(true,true,true,false);
+      hint.showPopover(); // Because hint is nested in popover2, popover3 should be hidden
+      assert_state_2(true,true,false,true);
+      popover1.hidePopover(); // Should close the hint, which is anchored to popover2
+      assert_state_2(false,false,false,false);
+    },'hint is not closed by pre-existing auto');
+  }
+  </script>
+</div>
+
+<div>
+  <div popover=hint>Hint
+    <div popover=hint>Nested hint</div>
+  </div>
+  <script>
+  test(() => {
+    const hint1 = document.currentScript.parentElement.querySelectorAll('[popover=hint]')[0];
+    const hint2 = document.currentScript.parentElement.querySelectorAll('[popover=hint]')[1];
+    hint1.showPopover();
+    assert_true(hint1.matches(':open'));
+    assert_false(hint2.matches(':open'));
+    hint2.showPopover();
+    assert_false(hint1.matches(':open'));
+    assert_true(hint2.matches(':open'));
+    hint2.hidePopover();
+  },'If a popover=hint is shown, it should hide any other open popover=hint pop-ups, including ancestral pop-ups. (You can\'t nest popover=hint)');
+  </script>
+</div>
+
+<div>
+  <div popover="hint">Hint
+    <div popover>Nested auto (note - never visible, since inside display:none subtree)</div>
+  </div>
+  <script>
+  test(() => {
+    const hint = document.currentScript.parentElement.querySelector('[popover=hint]');
+    const auto = document.currentScript.parentElement.querySelector('[popover=""]');
+    hint.showPopover();
+    assert_true(hint.matches(':open'));
+    assert_false(auto.matches(':open'));
+    auto.showPopover();
+    assert_false(hint.matches(':open'));
+    assert_true(auto.matches(':open'));
+    auto.hidePopover();
+  },'If a popover=auto is shown, it should hide any open popover=hint, including if the popover=hint is an ancestral pop-up of the popover=auto. (You can\'t nest a popover=auto inside a popover=hint)');
+  </script>
+</div>
+
+<div>
+  <div popover>Auto
+    <div popover>Nested Auto</div>
+    <div popover=hint>Nested hint</div>
+  </div>
+  <script>
+  test(() => {
+    const auto = document.currentScript.parentElement.querySelectorAll('[popover=""]')[0];
+    const auto2 = document.currentScript.parentElement.querySelectorAll('[popover=""]')[1];
+    const hint = document.currentScript.parentElement.querySelector('[popover=hint]');
+    auto.showPopover();
+    auto2.showPopover();
+    assert_true(auto.matches(':open'));
+    assert_true(auto2.matches(':open'));
+    hint.showPopover(); // This should hide auto2, since it is nested in auto1.
+    assert_true(auto.matches(':open'));
+    assert_false(auto2.matches(':open'));
+    assert_true(hint.matches(':open'));
+    auto.hidePopover(); // Should hide both auto and hint.
+    assert_false(auto.matches(':open'));
+    assert_false(hint.matches(':open'));
+  },'If you: a) show a popover=auto (call it D), then b) show a descendent popover=hint of D (call it T), then c) hide D, then T should be hidden. (A popover=hint can be nested inside a popover=auto)');
+  </script>
+</div>
+
+<div>
+  <div popover>Auto</div>
+  <div popover=hint>Non-Nested hint</div>
+  <script>
+  test(() => {
+    const auto = document.currentScript.parentElement.querySelector('[popover=""]');
+    const hint = document.currentScript.parentElement.querySelector('[popover=hint]');
+    auto.showPopover();
+    hint.showPopover();
+    assert_true(auto.matches(':open'));
+    assert_true(hint.matches(':open'));
+    auto.hidePopover();
+    assert_false(auto.matches(':open'));
+    assert_true(hint.matches(':open'));
+    hint.hidePopover();
+  },'If you: a) show a popover=auto (call it D), then b) show a non-descendent popover=hint of D (call it T), then c) hide D, then T should be left showing. (Non-nested popover=hint can stay open when unrelated popover=autos are hidden)');
+  </script>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
index 0df24ccd..404b893b 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
@@ -107,3 +107,10 @@
     window.addEventListener('load',show,{once:true});
   }
 }
+function popoverHintSupported() {
+  // TODO(crbug.com/1416284): This function should be removed, and
+  // any calls replaced with `true`, once popover=hint ships.
+  const testElement = document.createElement('div');
+  testElement.popover = 'hint';
+  return testElement.popover === 'hint';
+}
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/cross-origin-iframe.sub.html.ini b/third_party/blink/web_tests/external/wpt/intersection-observer/cross-origin-iframe.sub.html.ini
index 87d0f1d0..49c5d9b0 100644
--- a/third_party/blink/web_tests/external/wpt/intersection-observer/cross-origin-iframe.sub.html.ini
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/cross-origin-iframe.sub.html.ini
@@ -10,11 +10,10 @@
 
   [topDocument.scrollingElement.scrollTop = 100]
     expected:
+      if (product == "content_shell") and (os == "win") and (port == "win11"): [FAIL, PASS]
       if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): FAIL
-      if (product == "content_shell") and (os == "mac"): [PASS, FAIL]
 
   [topDocument.scrollingElement.scrollTop = 200]
     expected:
-      if (product == "content_shell") and (os == "win") and (port == "win11"): PASS
-      if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
+      if (product == "content_shell") and (os == "win"): PASS
       FAIL
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html.ini b/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html.ini
new file mode 100644
index 0000000..9900ba92
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/v2/delay-test.html.ini
@@ -0,0 +1,3 @@
+[delay-test.html]
+  ['delay' parameter throttles frequency of notifications.]
+    expected: [FAIL, PASS]
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/entries-for-object-frame-options-deny.html b/third_party/blink/web_tests/external/wpt/resource-timing/entries-for-object-frame-options-deny.html
new file mode 100644
index 0000000..a69b642
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/entries-for-object-frame-options-deny.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<meta name="timeout" content="long">
+<link rel="author" title="Noam Rosenthal" href="noam@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/entry-invariants.js"></script>
+</head>
+<body>
+<script>
+const {REMOTE_ORIGIN} = get_host_info();
+
+promise_test(async t => {
+  const success_url = new URL("/resource-timing/resources/object-frame-options-200.asis", REMOTE_ORIGIN).href;
+  const fail_url = new URL("/resource-timing/resources/object-frame-options-403.asis", REMOTE_ORIGIN).href;
+  const load_object = async url => {
+    const object = document.createElement("object");
+    object.data = url;
+    document.body.appendChild(object);
+    t.add_cleanup(() => object.remove());
+    await new Promise(resolve => {
+      object.onload = object.onerror = resolve;
+    });
+  };
+
+  await Promise.all([success_url, fail_url].map(load_object));
+  assert_equals(performance.getEntriesByName(success_url).length, 1);
+  assert_equals(performance.getEntriesByName(fail_url).length, 1);
+}, "Test that object elements with X-Frame-Options: Deny produce resource timing entries");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/iframe-sequence-of-events.html b/third_party/blink/web_tests/external/wpt/resource-timing/iframe-sequence-of-events.html
index 02d1c36..5d719fb9 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/iframe-sequence-of-events.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/iframe-sequence-of-events.html
@@ -3,6 +3,7 @@
 <title>Test the sequence of events when reporting iframe timing.</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/entry-invariants.js"></script>
 <script src="resources/frame-timing.js"></script>
 <script src="/common/utils.js"></script>
 <script src="/common/get-host-info.sub.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/frame-timing.js b/third_party/blink/web_tests/external/wpt/resource-timing/resources/frame-timing.js
index 019bd42..65710e2 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resources/frame-timing.js
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/frame-timing.js
@@ -25,8 +25,8 @@
 
 
 function test_frame_timing_change_src(type,
-                                      origin1 = document.origin,
-                                      origin2 = document.origin,
+                                      origin1 = location.origin,
+                                      origin2 = location.origin,
                                       tao = false, label = '') {
   const uid = token();
   promise_test(async t => {
@@ -59,5 +59,11 @@
 
     const entries = performance.getEntries().filter(e => e.name.includes(uid));
     assert_equals(entries.length, 2);
+    for (const entry of entries) {
+      if (tao || entry.name.startsWith(location.origin))
+        invariants.assert_tao_pass_no_redirect_http(entry);
+      else
+        invariants.assert_tao_failure_resource(entry);
+    }
   }, label || `A ${type} should report separate RT entries if its src changed from the outside`);
 }
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/object-frame-options-200.asis b/third_party/blink/web_tests/external/wpt/resource-timing/resources/object-frame-options-200.asis
new file mode 100644
index 0000000..12244532
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/object-frame-options-200.asis
@@ -0,0 +1,6 @@
+HTTP/1.0 200 OK
+Content-Type: text/html
+X-Frame-Options: DENY
+Content-Security-Policy: frame-ancestors 'none'
+
+Hello
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/object-frame-options-403.asis b/third_party/blink/web_tests/external/wpt/resource-timing/resources/object-frame-options-403.asis
new file mode 100644
index 0000000..fd64f0bc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/object-frame-options-403.asis
@@ -0,0 +1,6 @@
+HTTP/1.0 403 OK
+Content-Type: text/html
+X-Frame-Options: DENY
+Content-Security-Policy: frame-ancestors 'none'
+
+Hello
diff --git a/third_party/blink/web_tests/platform/mac/wpt_internal/compute-pressure/losing-focus-suspends-data-delivery.https.window-expected.txt b/third_party/blink/web_tests/platform/mac/wpt_internal/compute-pressure/losing-focus-suspends-data-delivery.https.window-expected.txt
new file mode 100644
index 0000000..0a85053
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/wpt_internal/compute-pressure/losing-focus-suspends-data-delivery.https.window-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Observer should not receive PressureRecord if page loses focus assert_equals: expected "fair" but got "nominal"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/popover-disabled/popover-disabled-expected.txt b/third_party/blink/web_tests/virtual/popover-disabled/popover-disabled-expected.txt
deleted file mode 100644
index 58123bd..0000000
--- a/third_party/blink/web_tests/virtual/popover-disabled/popover-disabled-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-CONSOLE ERROR: Found a 'popover' attribute. If you are testing the popover API, you must enable Experimental Web Platform Features. If not, note that custom attributes must start with 'data-': https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute. This usage will *likely cause site breakage* when the popover API ships: https://chromestatus.com/feature/5463833265045504.
-CONSOLE ERROR: Found a 'popover' attribute. If you are testing the popover API, you must enable Experimental Web Platform Features. If not, note that custom attributes must start with 'data-': https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute. This usage will *likely cause site breakage* when the popover API ships: https://chromestatus.com/feature/5463833265045504.
-CONSOLE ERROR: Found a 'popover' attribute. If you are testing the popover API, you must enable Experimental Web Platform Features. If not, note that custom attributes must start with 'data-': https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute. This usage will *likely cause site breakage* when the popover API ships: https://chromestatus.com/feature/5463833265045504.
-CONSOLE ERROR: Found a 'popover' attribute. If you are testing the popover API, you must enable Experimental Web Platform Features. If not, note that custom attributes must start with 'data-': https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute. This usage will *likely cause site breakage* when the popover API ships: https://chromestatus.com/feature/5463833265045504.
-CONSOLE ERROR: Found a 'popover' attribute. If you are testing the popover API, you must enable Experimental Web Platform Features. If not, note that custom attributes must start with 'data-': https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute. This usage will *likely cause site breakage* when the popover API ships: https://chromestatus.com/feature/5463833265045504.
-This is a testharness.js-based test.
-PASS Basic tests
-PASS <div popover="auto">This content should be visible, if HTMLPopoverAttribute is disabled</div> should be stylable/visible and not display:none
-PASS <div popover="async">This content should be visible, if HTMLPopoverAttribute is disabled</div> should be stylable/visible and not display:none
-PASS <div popover="invalid">This content should be visible, if HTMLPopoverAttribute is disabled</div> should be stylable/visible and not display:none
-PASS <div popover="">This content should be visible, if HTMLPopoverAttribute is disabled</div> should be stylable/visible and not display:none
-PASS <div popover="" id="foo">This content should be visible, if HTMLPopoverAttribute is disabled</div> should be stylable/visible and not display:none
-PASS The popover IDL attribute should not be present on Element
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/popover-hint-disabled/README.md b/third_party/blink/web_tests/virtual/popover-hint-disabled/README.md
new file mode 100644
index 0000000..7d98127
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/popover-hint-disabled/README.md
@@ -0,0 +1,5 @@
+# Overview
+
+This suite runs a small subset of tests with `--enable-features=HTMLPopoverAttribute`
+and `--disable-features=HTMLPopoverHint`, to make sure no popover=hint behavior is
+inadvertently exposed when the feature is disabled.
diff --git a/third_party/blink/web_tests/virtual/popover-hint-disabled/popover-hint-disabled.html b/third_party/blink/web_tests/virtual/popover-hint-disabled/popover-hint-disabled.html
new file mode 100644
index 0000000..88950768
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/popover-hint-disabled/popover-hint-disabled.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src='../../resources/testharness.js'></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<div popover=hint id=target>This should be a MANUAL popover, if HTMLPopoverAttribute is enabled and HTMLPopoverHint is disabled</div>
+
+<script>
+const target = document.getElementById('target');
+test(() => {
+  assert_false(target.matches(':open'),"The :open pseudo class should be available and false");
+  assert_true(target.matches(':closed'),"The :closed pseudo class should be available and true");
+},'popover=hint should still function as a popover');
+
+test(() => {
+  assert_true(target.matches(':closed'));
+  target.showPopover(); // Shouldn't throw
+  assert_true(target.matches(':open'));
+  target.hidePopover();
+  assert_true(target.matches(':closed'));
+},`popover=hint should function as a manual popover`);
+
+test(() => {
+  assert_equals(target.popover,'manual','With HTMLPopoverHint disabled, popover=hint should be treated as a manual popover');
+  assert_equals(target.getAttribute('popover'),'hint','The content attribute should still be "hint"');
+},`popover=hint should *not* be feature detected`);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/compute-pressure/losing-focus-suspends-data-delivery.https.window.js.ini b/third_party/blink/web_tests/wpt_internal/compute-pressure/losing-focus-suspends-data-delivery.https.window.js.ini
new file mode 100644
index 0000000..9264d33
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/compute-pressure/losing-focus-suspends-data-delivery.https.window.js.ini
@@ -0,0 +1,3 @@
+[losing-focus-suspends-data-delivery.https.window.html]
+  [Observer should not receive PressureRecord if page loses focus]
+    expected: FAIL
diff --git a/third_party/blink/web_tests/wpt_internal/view-transition-on-navigation/resources/transition-to-prerender.html b/third_party/blink/web_tests/wpt_internal/view-transition-on-navigation/resources/transition-to-prerender.html
new file mode 100644
index 0000000..688318a
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/view-transition-on-navigation/resources/transition-to-prerender.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>View transitions: prerender navigation helper</title>
+<link rel="help" href="https://github.com/WICG/view-transitions">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<meta name="view-transition" content="same-origin">
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<style>
+html { background: red; }
+div {
+  width: 200px;
+  height: 200px;
+  background: red;
+  color: black;
+  position: absolute;
+  top: 40px;
+  right: 8px;
+  view-transition-name: target;
+}
+
+::view-transition {
+  background: lightblue;
+}
+
+::view-transition-group(root) {
+  visibility: hidden;
+  animation-duration: 500s;
+}
+
+::view-transition-group(target) {
+  animation-play-state: paused;
+}
+::view-transition-new(target) {
+  animation: unset;
+  opacity: 1;
+}
+::view-transition-old(target) {
+  animation: unset;
+  opacity: 0;
+}
+</style>
+<div id=target>
+  There should be a green square on the left side of the screen,
+  on a field of lightblue. There should be no red in the final state.
+</div>
+
+<script>
+if (document.prerendering) {
+  target.style.background = "green";
+}
+const channel = new PrerenderChannel('prerender-channel');
+channel.postMessage('loaded!');
+</script>
+
+
diff --git a/third_party/blink/web_tests/wpt_internal/view-transition-on-navigation/transition-to-prerender-manual.html b/third_party/blink/web_tests/wpt_internal/view-transition-on-navigation/transition-to-prerender-manual.html
new file mode 100644
index 0000000..1b5f38c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/view-transition-on-navigation/transition-to-prerender-manual.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>View transitions: prerender navigation</title>
+<link rel="help" href="https://github.com/WICG/view-transitions">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<!-- TODO(crbug.com/1418681). This test should work automatically,
+     but doesn't due to the referenced bug.
+<link rel="match" href="transition-to-prerender-ref.html">
+-->
+<script src="/common/utils.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<meta name="view-transition" content="same-origin">
+
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+html { background: red; }
+#target {
+  width: 200px;
+  height: 200px;
+  background: black;
+  color: white;
+  position: absolute;
+  top: 40px;
+  view-transition-name: target;
+}
+</style>
+
+<div id=target></div>
+
+<script>
+
+const uid = token();
+
+function startTest() {
+  const old_url = "/transition-to-prerender-manual.html";
+  const new_url = `/resources/transition-to-prerender.html?uid=${uid}`;
+  window.location.href = window.location.href.replace(old_url, new_url);
+}
+
+async function waitForPrerender() {
+  const channel = new PrerenderChannel('prerender-channel', uid);
+
+  const gotMessage = new Promise(resolve => {
+      channel.addEventListener('message', resolve)
+    }, {
+      once: true
+  });
+
+  startPrerendering(`resources/transition-to-prerender.html?uid=${uid}`);
+  await gotMessage;
+  startTest();
+}
+
+onload = waitForPrerender;
+</script>
+
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index b795818..a85bf36 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 3e8727238bae3c069bd71cfb3b2bbaa98b55f05b
+Revision: 7a997fb25352a1c0cf99020ee82c4dfeae251eda
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
index a3a6855..86142883 100644
--- a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
+++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/strings/stringprintf.h"
 #include "client/settings.h"
 #include "handler/linux/capture_snapshot.h"
 #include "handler/minidump_to_upload_parameters.h"
@@ -246,6 +247,16 @@
   // crash_reporter needs to know the pid and uid of the crashing process.
   std::vector<std::string> argv({"/sbin/crash_reporter"});
 
+  // Used to distinguish between non-fatal and fatal crashes.
+  const ExceptionSnapshot* const exception_snapshot = snapshot->Exception();
+  if (exception_snapshot) {
+    // convert to int32, since crashpad uses -1 as a signal for non-fatal
+    // crashes.
+    argv.push_back(base::StringPrintf(
+        "--chrome_signal=%d",
+        static_cast<int32_t>(exception_snapshot->Exception())));
+  }
+
   argv.push_back("--chrome_memfd=" + std::to_string(file_writer.fd()));
 
   const pid_t pid = process_snapshot->ProcessID();
diff --git a/third_party/crashpad/crashpad/tools/BUILD.gn b/third_party/crashpad/crashpad/tools/BUILD.gn
index 4c5d446b..3a042ed 100644
--- a/third_party/crashpad/crashpad/tools/BUILD.gn
+++ b/third_party/crashpad/crashpad/tools/BUILD.gn
@@ -25,6 +25,22 @@
   deps = [ "$mini_chromium_source_parent:base" ]
 }
 
+crashpad_executable("dump_minidump_annotations") {
+  sources = [ "dump_minidump_annotations.cc" ]
+
+  deps = [
+    ":tool_support",
+    "../client",
+    "../snapshot",
+    "../util",
+  ]
+
+  if (crashpad_is_win) {
+    cflags =
+        [ "/wd4201" ]  # nonstandard extension used : nameless struct/union
+  }
+}
+
 if (!crashpad_is_ios) {
   crashpad_executable("crashpad_database_util") {
     sources = [ "crashpad_database_util.cc" ]
diff --git a/third_party/crashpad/crashpad/tools/dump_minidump_annotations.cc b/third_party/crashpad/crashpad/tools/dump_minidump_annotations.cc
new file mode 100644
index 0000000..1c26c1b
--- /dev/null
+++ b/third_party/crashpad/crashpad/tools/dump_minidump_annotations.cc
@@ -0,0 +1,147 @@
+// Copyright 2023 The Crashpad Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <getopt.h>
+
+#include "base/files/file_path.h"
+#include "client/annotation.h"
+#include "util/file/file_reader.h"
+#include "snapshot/minidump/process_snapshot_minidump.h"
+#include "tools/tool_support.h"
+
+namespace crashpad {
+namespace {
+
+void Usage(const base::FilePath& me) {
+  // clang-format off
+  fprintf(stderr,
+"Usage: %" PRFilePath " [OPTION]... PATH\n"
+"Dump annotations from minidumps.\n"
+"\n"
+"      --help                      display this help and exit\n"
+"      --version                   output version information and exit\n",
+          me.value().c_str());
+  // clang-format on
+  ToolSupport::UsageTail(me);
+}
+
+struct Options {
+  const char* minidump;
+};
+
+int DumpMinidumpAnnotationsMain(int argc, char* argv[]) {
+  const base::FilePath argv0(
+      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
+  const base::FilePath me(argv0.BaseName());
+
+  enum OptionFlags {
+    // Long options without short equivalents.
+    kOptionLastChar = 255,
+    kOptionMinidump,
+
+    // Standard options.
+    kOptionHelp = -2,
+    kOptionVersion = -3,
+  };
+
+  static constexpr option long_options[] = {
+      {"minidump", required_argument, nullptr, kOptionMinidump},
+      {"help", no_argument, nullptr, kOptionHelp},
+      {"version", no_argument, nullptr, kOptionVersion},
+      {nullptr, 0, nullptr, 0},
+  };
+
+  Options options = {};
+
+  int opt;
+  while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
+    switch (opt) {
+      case kOptionMinidump: {
+        options.minidump = optarg;
+        break;
+      }
+      case kOptionHelp: {
+        Usage(me);
+        return EXIT_SUCCESS;
+      }
+      case kOptionVersion: {
+        ToolSupport::Version(me);
+        return EXIT_SUCCESS;
+      }
+      default: {
+        ToolSupport::UsageHint(me, nullptr);
+        return EXIT_FAILURE;
+      }
+    }
+  }
+  argc -= optind;
+  argv += optind;
+
+  if (!options.minidump) {
+    ToolSupport::UsageHint(me, "--minidump is required");
+    return EXIT_FAILURE;
+  }
+
+  FileReader reader;
+  if (!reader.Open(base::FilePath(
+      ToolSupport::CommandLineArgumentToFilePathStringType(
+          options.minidump)))) {
+    return EXIT_FAILURE;
+  }
+
+  ProcessSnapshotMinidump snapshot;
+  if (!snapshot.Initialize(&reader)) {
+    return EXIT_FAILURE;
+  }
+
+  for (const ModuleSnapshot* module : snapshot.Modules()) {
+    printf("Module: %s\n", module->Name().c_str());
+    printf("  Simple Annotations\n");
+    for (const auto& kv : module->AnnotationsSimpleMap()) {
+      printf("    simple_annotations[\"%s\"] = %s\n",
+             kv.first.c_str(), kv.second.c_str());
+    }
+
+    printf("  Vectored Annotations\n");
+    int index = 0;
+    for (const std::string& annotation : module->AnnotationsVector()) {
+      printf("    vectored_annotations[%d] = %s\n", index, annotation.c_str());
+      index++;
+    }
+
+    printf("  Annotation Objects\n");
+    for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
+      printf("    annotation_objects[\"%s\"] = ", annotation.name.c_str());
+      if (annotation.type != static_cast<uint16_t>(Annotation::Type::kString)) {
+
+        printf("<non-string value, not printing>\n");
+        continue;
+      }
+
+      std::string value(reinterpret_cast<const char*>(annotation.value.data()),
+                        annotation.value.size());
+
+      printf("%s\n", value.c_str());
+    }
+  }
+
+  return EXIT_SUCCESS;
+}
+
+}  // namespace
+}  // namespace crashpad
+
+int main(int argc, char* argv[]) {
+  return crashpad::DumpMinidumpAnnotationsMain(argc, argv);
+}
diff --git a/third_party/wpt_tools/PRESUBMIT.py b/third_party/wpt_tools/PRESUBMIT.py
index 87ca0efa..0eb104d6 100644
--- a/third_party/wpt_tools/PRESUBMIT.py
+++ b/third_party/wpt_tools/PRESUBMIT.py
@@ -10,6 +10,10 @@
 
 USE_PYTHON3 = True
 
+import pathlib
+import textwrap
+
+
 def _TestWPTLint(input_api, output_api):
   # We test 'wpt lint' by deferring to the web_tests/external presubmit test,
   # which runs 'wpt lint' against web_tests/external/wpt.
@@ -55,10 +59,47 @@
   return []
 
 
+def _TestWPTRolled(input_api, output_api):
+  """Warn developers making manual changes to `wpt_tools/`."""
+  subject, *_ = input_api.change.DescriptionText().splitlines()
+  if input_api.re.search(r'\broll wpt tooling\b', subject,
+                         input_api.re.IGNORECASE):
+    return []
+
+  include_file = input_api.os_path.join(input_api.PresubmitLocalPath(),
+                                        'WPTIncludeList')
+  rolled_files = {pathlib.PurePosixPath(line)
+                  for line in input_api.ReadFile(include_file).splitlines()}
+  wpt_dir = pathlib.Path(input_api.PresubmitLocalPath()) / 'wpt'
+
+  def exclude_unrolled_files(affected_file):
+    try:
+      path_from_wpt = pathlib.Path(
+        affected_file.AbsoluteLocalPath()).relative_to(wpt_dir)
+      return pathlib.PurePosixPath(path_from_wpt.as_posix()) in rolled_files
+    except ValueError:
+      # Exclude file relative to `wpt_tools` but not to `wpt_tools/wpt`.
+      return False
+
+  if input_api.AffectedFiles(file_filter=exclude_unrolled_files):
+    message = textwrap.dedent(
+      """\
+      Thanks for your patch to `//third_party/wpt_tools/wpt`. This directory is
+      semiregularly overwritten by rolls from the upstream repo:
+        https://github.com/web-platform-tests/wpt
+
+      Please submit your change upstream as a pull request instead, then run
+      `//third_party/wpt_tools/roll_wpt.py` to pick up the change.
+      """)
+    return [output_api.PresubmitPromptWarning(message)]
+  return []
+
+
 def CheckChangeOnUpload(input_api, output_api):
   results = []
   results += _TestWPTLint(input_api, output_api)
   results += _TestWPTManifest(input_api, output_api)
+  results += _TestWPTRolled(input_api, output_api)
   return results
 
 
@@ -66,4 +107,5 @@
   results = []
   results += _TestWPTLint(input_api, output_api)
   results += _TestWPTManifest(input_api, output_api)
+  results += _TestWPTRolled(input_api, output_api)
   return results
diff --git a/third_party/zlib/contrib/optimizations/inffast_chunk.c b/third_party/zlib/contrib/optimizations/inffast_chunk.c
index 5b09487..a38e14d 100644
--- a/third_party/zlib/contrib/optimizations/inffast_chunk.c
+++ b/third_party/zlib/contrib/optimizations/inffast_chunk.c
@@ -1,5 +1,6 @@
 /* inffast_chunk.c -- fast decoding
  * Copyright (C) 1995-2017 Mark Adler
+ * Copyright 2023 The Chromium Authors
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -24,8 +25,8 @@
    Entry assumptions:
 
         state->mode == LEN
-        strm->avail_in >= INFLATE_FAST_MIN_INPUT (6 or 8 bytes)
-        strm->avail_out >= INFLATE_FAST_MIN_OUTPUT (258 bytes)
+        strm->avail_in >= INFLATE_FAST_MIN_INPUT (6 or 8 bytes + 7 bytes)
+        strm->avail_out >= INFLATE_FAST_MIN_OUTPUT (258 bytes + 2 bytes)
         start >= strm->avail_out
         state->bits < 8
         (state->hold >> state->bits) == 0
@@ -42,7 +43,7 @@
 
    Notes:
 
-    INFLATE_FAST_MIN_INPUT: 6 or 8 bytes
+    INFLATE_FAST_MIN_INPUT: 6 or 8 bytes + 7 bytes
 
     - The maximum input bits used by a length/distance pair is 15 bits for the
       length code, 5 bits for the length extra, 15 bits for the distance code,
@@ -64,11 +65,11 @@
 
           (state->hold >> state->bits) == 0
 
-    INFLATE_FAST_MIN_OUTPUT: 258 bytes
+    INFLATE_FAST_MIN_OUTPUT: 258 bytes + 2 bytes for literals = 260 bytes
 
     - The maximum bytes that a single length/distance pair can output is 258
       bytes, which is the maximum length that can be coded.  inflate_fast()
-      requires strm->avail_out >= 258 for each loop to avoid checking for
+      requires strm->avail_out >= 260 for each loop to avoid checking for
       available output space while decoding.
  */
 void ZLIB_INTERNAL inflate_fast_chunk_(strm, start)
@@ -124,22 +125,50 @@
     lmask = (1U << state->lenbits) - 1;
     dmask = (1U << state->distbits) - 1;
 
+#ifdef INFLATE_CHUNK_READ_64LE
+#define REFILL() do { \
+        Assert(bits < 64, "### Too many bits in inflate_fast."); \
+        hold |= read64le(in) << bits; \
+        in += 7; \
+        in -= bits >> 3; \
+        bits |= 56; \
+    } while (0)
+#endif
+
     /* decode literals and length/distances until end-of-block or not enough
        input data or output space */
     do {
-        if (bits < 15) {
 #ifdef INFLATE_CHUNK_READ_64LE
-            hold |= read64le(in) << bits;
-            in += 6;
-            bits += 48;
+        REFILL();
 #else
+        if (bits < 15) {
             hold += (unsigned long)(*in++) << bits;
             bits += 8;
             hold += (unsigned long)(*in++) << bits;
             bits += 8;
-#endif
         }
+#endif
         here = lcode + (hold & lmask);
+#ifdef INFLATE_CHUNK_READ_64LE
+        if (here->op == 0) {                    /* literal */
+            Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ?
+                    "inflate:         literal '%c'\n" :
+                    "inflate:         literal 0x%02x\n", here->val));
+            *out++ = (unsigned char)(here->val);
+            hold >>= here->bits;
+            bits -= here->bits;
+            here = lcode + (hold & lmask);
+            if (here->op == 0) {                /* literal */
+                Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ?
+                        "inflate:    2nd  literal '%c'\n" :
+                        "inflate:    2nd  literal 0x%02x\n", here->val));
+                *out++ = (unsigned char)(here->val);
+                hold >>= here->bits;
+                bits -= here->bits;
+                here = lcode + (hold & lmask);
+            }
+        }
+#endif
       dolen:
         op = (unsigned)(here->bits);
         hold >>= op;
@@ -155,33 +184,25 @@
             len = (unsigned)(here->val);
             op &= 15;                           /* number of extra bits */
             if (op) {
+#ifndef INFLATE_CHUNK_READ_64LE
                 if (bits < op) {
-#ifdef INFLATE_CHUNK_READ_64LE
-                    hold |= read64le(in) << bits;
-                    in += 6;
-                    bits += 48;
-#else
                     hold += (unsigned long)(*in++) << bits;
                     bits += 8;
-#endif
                 }
+#endif
                 len += (unsigned)hold & ((1U << op) - 1);
                 hold >>= op;
                 bits -= op;
             }
             Tracevv((stderr, "inflate:         length %u\n", len));
+#ifndef INFLATE_CHUNK_READ_64LE
             if (bits < 15) {
-#ifdef INFLATE_CHUNK_READ_64LE
-                hold |= read64le(in) << bits;
-                in += 6;
-                bits += 48;
-#else
                 hold += (unsigned long)(*in++) << bits;
                 bits += 8;
                 hold += (unsigned long)(*in++) << bits;
                 bits += 8;
-#endif
             }
+#endif
             here = dcode + (hold & dmask);
           dodist:
             op = (unsigned)(here->bits);
@@ -191,11 +212,11 @@
             if (op & 16) {                      /* distance base */
                 dist = (unsigned)(here->val);
                 op &= 15;                       /* number of extra bits */
+                /* we have two fast-path loads: 10+10 + 15+5 + 15 = 55,
+                   but we may need to refill here in the worst case */
                 if (bits < op) {
 #ifdef INFLATE_CHUNK_READ_64LE
-                    hold |= read64le(in) << bits;
-                    in += 6;
-                    bits += 48;
+                    REFILL();
 #else
                     hold += (unsigned long)(*in++) << bits;
                     bits += 8;
diff --git a/third_party/zlib/contrib/optimizations/inffast_chunk.h b/third_party/zlib/contrib/optimizations/inffast_chunk.h
index 39c771b8..cc861bd 100644
--- a/third_party/zlib/contrib/optimizations/inffast_chunk.h
+++ b/third_party/zlib/contrib/optimizations/inffast_chunk.h
@@ -1,6 +1,7 @@
 /* inffast_chunk.h -- header to use inffast_chunk.c
  * Copyright (C) 1995-2003, 2010 Mark Adler
  * Copyright (C) 2017 ARM, Inc.
+ * Copyright 2023 The Chromium Authors
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -11,16 +12,31 @@
 
 #include "inffast.h"
 
-/* INFLATE_FAST_MIN_INPUT: the minimum number of input bytes needed so that
-   we can safely call inflate_fast() with only one up-front bounds check. One
+/* INFLATE_FAST_MIN_INPUT:
+   The minimum number of input bytes needed so that we can safely call
+   inflate_fast() with only one up-front bounds check. One
    length/distance code pair (15 bits for the length code, 5 bits for length
    extra, 15 bits for the distance code, 13 bits for distance extra) requires
-   reading up to 48 input bits (6 bytes). The wide input data reading option
-   requires a little endian machine, and reads 64 input bits (8 bytes).
+   reading up to 48 input bits. Additionally, in the same iteraction, we may
+   decode two literals from the root-table (requiring MIN_OUTPUT = 258 + 2).
+
+   Each root-table entry is up to 10 bits, for a total of 68 input bits each
+   iteraction.
+
+   The refill variant reads 8 bytes from the buffer at a time, and advances
+   the input pointer by up to 7 bytes, ensuring there are at least 56-bits
+   available in the bit-buffer. The technique was documented by Fabian Giesen
+   on his blog as variant 4 in the article 'Reading bits in far too many ways':
+   https://fgiesen.wordpress.com/2018/02/20/
+
+   In the worst case, we may refill twice in the same iteraction, requiring
+   MIN_INPUT = 8 + 7.
 */
 #ifdef INFLATE_CHUNK_READ_64LE
 #undef INFLATE_FAST_MIN_INPUT
-#define INFLATE_FAST_MIN_INPUT 8
+#define INFLATE_FAST_MIN_INPUT 15
+#undef INFLATE_FAST_MIN_OUTPUT
+#define INFLATE_FAST_MIN_OUTPUT 260
 #endif
 
 void ZLIB_INTERNAL inflate_fast_chunk_ OF((z_streamp strm, unsigned start));
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 4fcaaab..d058a1a 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
 # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
-CLANG_REVISION = 'llvmorg-17-init-2387-g68e81d7e'
+CLANG_REVISION = 'llvmorg-17-init-3170-g6e30dffe'
 CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
diff --git a/tools/fuchsia/local-sdk.py b/tools/fuchsia/local-sdk.py
index 57b74965..c09ab58 100755
--- a/tools/fuchsia/local-sdk.py
+++ b/tools/fuchsia/local-sdk.py
@@ -4,6 +4,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import filecmp
 import hashlib
 import json
 import os
@@ -38,33 +39,28 @@
   Run('scripts/fx', 'build', 'sdk', 'build/images')
 
 
-def _CopyFilesIntoExistingDirectory(src, dst):
-  for entry in os.listdir(src):
-    src_path = os.path.join(src, entry)
-    dst_path = os.path.join(dst, entry)
-    if os.path.isdir(src_path):
-      if not os.path.exists(dst_path):
-        os.mkdir(dst_path)
-      _CopyFilesIntoExistingDirectory(src_path, dst_path)
-    else:
-      shutil.copy(src_path, dst_path)
+def Copy(src, dst):
+  if os.path.exists(dst) and filecmp.cmp(src, dst, shallow=False):
+    return
+  shutil.copy2(src, dst)
 
 
 def main(args):
-  if len(args) == 0 or len(args) > 2 or not os.path.isdir(args[0]):
+  if len(args) == 0 or not os.path.isdir(args[0]):
     print("""usage: %s <path_to_fuchsia_tree> [architecture]""" % SELF_FILE)
     return 1
 
-  target_archs = []
-  if len(args) > 1:
-    arch = args[1]
-    if arch not in ['x64', 'arm64']:
-      print('Unknown architecture: ' + arch)
-      print('Must be "x64" or "arm64".')
-      return 1
-    target_archs = [arch]
+  ALL_ARCHS = set(['x64', 'arm64'])
+  if len(args) == 1:
+    target_archs = ALL_ARCHS
   else:
-    target_archs = ['x64', 'arm64']
+    target_archs = set(args[1:])
+    unknown_archs = target_archs - ALL_ARCHS
+    if unknown_archs:
+      print(
+          f'Unknown architectures: {unknown_archs}. Known architectures: {ALL_ARCHS}'
+      )
+      return 1
 
   # Nuke the SDK from DEPS, put our just-built one there, and set a fake .hash
   # file. This means that on next gclient runhooks, we'll restore to the
@@ -99,7 +95,10 @@
       Run('scripts/sdk/gn/generate.py', '--archive', sdk_tar_path, '--output',
           sdk_gn_dir)
 
-      _CopyFilesIntoExistingDirectory(sdk_gn_dir, sdk_output_dir)
+      shutil.copytree(sdk_gn_dir,
+                      sdk_output_dir,
+                      copy_function=Copy,
+                      dirs_exist_ok=True)
 
       # Merge the manifests.
       manifest_path = os.path.join(sdk_output_dir, 'meta', 'manifest.json')
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 68248fb..7b75595 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -31211,6 +31211,7 @@
   <int value="16" label="Video encoding error"/>
   <int value="17" label="Projector transcription error"/>
   <int value="18" label="Low DriveFS free space quota"/>
+  <int value="19" label="Video encoder does not support reconfiguration"/>
 </enum>
 
 <enum name="EnhancedBookmarkViewMode">
@@ -42348,6 +42349,7 @@
   <int value="4477" label="FencedFrameConfigAttribute"/>
   <int value="4478" label="URLSearchParams_Has_Delete_MultipleArguments"/>
   <int value="4479" label="PaymentHandlerMinimalHeaderUX"/>
+  <int value="4480" label="PopoverTypeHint"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -58558,6 +58560,7 @@
   <int value="-1811949013" label="SameSiteByDefaultCookies:disabled"/>
   <int value="-1811394154" label="disable-webrtc-hw-vp8-encoding"/>
   <int value="-1810891359" label="SendTabToSelfManageDevicesLink:disabled"/>
+  <int value="-1810354047" label="GifRecording:enabled"/>
   <int value="-1810294310" label="AndroidPaymentApps:enabled"/>
   <int value="-1809969509" label="FilesSinglePartitionFormat:disabled"/>
   <int value="-1809891158" label="WebAuthenticationCable:enabled"/>
@@ -58680,7 +58683,6 @@
   <int value="-1731997767" label="SharingHubDesktopAppMenu:enabled"/>
   <int value="-1731267886" label="AutofillEnableStickyPaymentsBubble:enabled"/>
   <int value="-1731149013" label="AndroidMessagesIntegration:enabled"/>
-  <int value="-1730755478" label="ClosedTabCache:disabled"/>
   <int value="-1729926412" label="enable-webusb-notifications"/>
   <int value="-1729808721" label="SubresourceRedirect:enabled"/>
   <int value="-1728605240" label="UseXpsForPrintingFromPdf:enabled"/>
@@ -58815,6 +58817,8 @@
   <int value="-1646653199" label="WallpaperGooglePhotosIntegration:enabled"/>
   <int value="-1646196271" label="WebRtcStatsReportIdl:disabled"/>
   <int value="-1646016597" label="IsolatePrerenders:disabled"/>
+  <int value="-1646002522"
+      label="ChromeOSAmbientModeManagedScreensaver:enabled"/>
   <int value="-1645071473" label="ChromeColors:disabled"/>
   <int value="-1644308778" label="WASAPIRawAudioCapture:disabled"/>
   <int value="-1643933608" label="SyncAutofillWalletOfferData:enabled"/>
@@ -59158,6 +59162,7 @@
   <int value="-1466206556" label="ArcDocumentsProviderUnknownSize:enabled"/>
   <int value="-1466100014" label="EchePhoneHubPermissionsOnboarding:disabled"/>
   <int value="-1465172796" label="CCTModule:disabled"/>
+  <int value="-1465138299" label="GifRecording:disabled"/>
   <int value="-1464860971" label="BluetoothCoredump:enabled"/>
   <int value="-1464832541" label="EnableFuzzyAppSearch:disabled"/>
   <int value="-1463489219" label="OfflinePagesCTSuppressNotifications:enabled"/>
@@ -62211,6 +62216,8 @@
   <int value="255806443" label="GetDisplayMediaSet:disabled"/>
   <int value="256753680" label="ChromeKioskEnableLacros:disabled"/>
   <int value="257206347" label="GuestOSGenericInstaller:disabled"/>
+  <int value="257348975"
+      label="ChromeOSAmbientModeManagedScreensaver:disabled"/>
   <int value="258621334"
       label="HappinessTrackingSurveysForDesktopDemo:disabled"/>
   <int value="259021228" label="OffMainThreadFetch:disabled"/>
@@ -63632,7 +63639,6 @@
   <int value="1065451331" label="NtpRealboxPedals:disabled"/>
   <int value="1065481614" label="SystemEmojiPickerGIFSupport:disabled"/>
   <int value="1065547630" label="EnableInputNoiseCancellationUi:disabled"/>
-  <int value="1065553214" label="ClosedTabCache:enabled"/>
   <int value="1067618884" label="enable-experimental-input-view-features"/>
   <int value="1067990299" label="enable-ui-devtools"/>
   <int value="1069325321" label="CrostiniGpuSupport:enabled"/>
diff --git a/tools/metrics/histograms/histograms_index.txt b/tools/metrics/histograms/histograms_index.txt
index ec3798c..d48203b 100644
--- a/tools/metrics/histograms/histograms_index.txt
+++ b/tools/metrics/histograms/histograms_index.txt
@@ -29,6 +29,7 @@
 tools/metrics/histograms/metadata/cookie/histograms.xml
 tools/metrics/histograms/metadata/cras/histograms.xml
 tools/metrics/histograms/metadata/cros/histograms.xml
+tools/metrics/histograms/metadata/cros_audio/histograms.xml
 tools/metrics/histograms/metadata/cros_ml/histograms.xml
 tools/metrics/histograms/metadata/cross_device/histograms.xml
 tools/metrics/histograms/metadata/crostini/histograms.xml
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index fc0b76fb..b236042 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -2431,7 +2431,7 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.Leipzig.FillingAssistanceOrigin"
+<histogram name="Autofill.Leipzig.FillingAssistanceCategory"
     enum="AutofillCategoryResolvedFillingAssistance" expires_after="2023-12-12">
   <owner>koerber@chromium.org</owner>
   <owner>fleimgruber@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 93540b5..e9a7426 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -164,6 +164,36 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Canvas.2DLayerBridge.Compression.DecompressionTime"
+    units="ms" expires_after="2023-08-08">
+  <owner>lizeb@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <summary>
+    When a compressed hibernated 2D canvas snapshot is decompressed, duration in
+    ms. Reported each time a canvas snapshot is decompressed.
+  </summary>
+</histogram>
+
+<histogram name="Blink.Canvas.2DLayerBridge.Compression.Ratio" units="%"
+    expires_after="2023-08-08">
+  <owner>lizeb@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <summary>
+    When a hibernated 2D canvas snapshot is compressed, the compression ratio
+    achieved. Reported each time a canvas snapshot is compressed.
+  </summary>
+</histogram>
+
+<histogram name="Blink.Canvas.2DLayerBridge.Compression.SnapshotSizeKb"
+    units="KB" expires_after="2023-08-08">
+  <owner>lizeb@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <summary>
+    When a hibernated 2D canvas snapshot is compressed, the size before
+    compression. Reported each time a canvas snapshot is compressed.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Canvas.2DLayerBridge.WillReadFrequently"
     enum="BooleanWillReadFrequently" expires_after="2024-04-01">
   <owner>aaronhk@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
index 35893e4..80b4b3d2c 100644
--- a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
@@ -75,26 +75,6 @@
   </summary>
 </histogram>
 
-<histogram name="ChromeOS.Settings.Device.Audio.InputMuteStateChange"
-    enum="AudioMuteButtonAction" expires_after="2024-02-14">
-  <owner>gavinwill@chromium.org</owner>
-  <owner>cros-peripherals@google.com</owner>
-  <summary>
-    Recorded when the user changes the input audio mute state by pressing the
-    mute button on the Audio Settings page.
-  </summary>
-</histogram>
-
-<histogram name="ChromeOS.Settings.Device.Audio.OutputMuteStateChange"
-    enum="AudioMuteButtonAction" expires_after="2024-02-14">
-  <owner>gavinwill@chromium.org</owner>
-  <owner>cros-peripherals@google.com</owner>
-  <summary>
-    Recorded when the user changes the output audio mute state by pressing the
-    mute button on the Audio Settings page.
-  </summary>
-</histogram>
-
 <histogram name="ChromeOS.Settings.Device.KeyboardAutoRepeatEnabled"
     enum="BooleanEnabled" expires_after="2023-11-01">
   <owner>ckincaid@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 3f0641222..b36e3a5 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -940,7 +940,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.ContentDuplication.Position{Index}"
-    enum="Boolean" expires_after="2023-03-01">
+    enum="Boolean" expires_after="2023-09-01">
   <obsolete>
     Deprecated in 11/2022. Replaced by ContentDuplication2 metrics.
   </obsolete>
@@ -958,7 +958,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.ContentDuplication.{Range}" units="%"
-    expires_after="2023-03-01">
+    expires_after="2023-09-01">
   <obsolete>
     Deprecated in 11/2022. Replaced by ContentDuplication2 metrics.
   </obsolete>
@@ -976,7 +976,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.ContentDuplication2.Position{Index}"
-    enum="Boolean" expires_after="2023-03-01">
+    enum="Boolean" expires_after="2023-09-01">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -993,7 +993,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.ContentDuplication2.{Range}" units="%"
-    expires_after="2023-03-01">
+    expires_after="2023-09-01">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1227,7 +1227,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.NoticeCardFulfilled2" enum="Boolean"
-    expires_after="2023-03-01">
+    expires_after="2023-09-01">
   <owner>vincb@google.com</owner>
   <owner>feed@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cros_audio/OWNERS b/tools/metrics/histograms/metadata/cros_audio/OWNERS
new file mode 100644
index 0000000..2c65f4fc
--- /dev/null
+++ b/tools/metrics/histograms/metadata/cros_audio/OWNERS
@@ -0,0 +1,5 @@
+per-file OWNERS=file://tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+
+# Prefer sending CLs to the owners listed below.
+# Use chromium-metrics-reviews@google.com as a backup.
+gavinwill@chromium.org
\ No newline at end of file
diff --git a/tools/metrics/histograms/metadata/cros_audio/histograms.xml b/tools/metrics/histograms/metadata/cros_audio/histograms.xml
new file mode 100644
index 0000000..df77163
--- /dev/null
+++ b/tools/metrics/histograms/metadata/cros_audio/histograms.xml
@@ -0,0 +1,47 @@
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<!--
+This file is used to generate a comprehensive list of histograms for
+functionality in CrosAudioConfig.
+
+For best practices on writing histogram descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
+
+Prefer owners from the OWNERS file in this directory. If you need a metrics
+reviewer, please send CLs to chromium-metrics-reviews@google.com rather than to
+specific individuals. These CLs will be automatically reassigned to a reviewer
+within about 5 minutes. This approach helps the metrics team to load-balance
+incoming reviews. Googlers can read more about this at go/gwsq-gerrit.
+-->
+
+<histogram-configuration>
+
+<histograms>
+
+<histogram name="ChromeOS.CrosAudioConfig.InputMuteStateChange"
+    enum="AudioMuteButtonAction" expires_after="2024-02-14">
+  <owner>gavinwill@chromium.org</owner>
+  <owner>cros-peripherals@google.com</owner>
+  <summary>
+    Recorded when the user changes the input audio mute state from a WebUI, such
+    as the OS Audio Settings page.
+  </summary>
+</histogram>
+
+<histogram name="ChromeOS.CrosAudioConfig.OutputMuteStateChange"
+    enum="AudioMuteButtonAction" expires_after="2024-02-14">
+  <owner>gavinwill@chromium.org</owner>
+  <owner>cros-peripherals@google.com</owner>
+  <summary>
+    Recorded when the user changes the output audio mute state from a WebUI,
+    such as the OS Audio Settings page.
+  </summary>
+</histogram>
+
+</histograms>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index c5f9c6f01..9ee34631 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -45,6 +45,22 @@
   <variant name=".Utility" summary="a Utility process"/>
 </variants>
 
+<variants name="UtilityProcessType">
+  <variant name="AudioService">
+    <owner>olka@chromium.org</owner>
+    <owner>fhernqvist@google.com</owner>
+    <owner>tguilbert@chromium.org</owner>
+    <owner>webrtc-audio-uma@google.com</owner>
+  </variant>
+  <variant name="PaintPreviewCompositor">
+    <owner>ckitagawa@chromium.org</owner>
+    <owner>fredmello@chromium.org</owner>
+  </variant>
+  <variant name="Utility">
+    <owner>jam@chromium.org</owner>
+  </variant>
+</variants>
+
 <histogram name="HeapProfiling.AndroidStackUnwinder"
     enum="AndroidStackUnwinder" expires_after="2023-07-09">
   <owner>joenotcharles@chromium.org</owner>
@@ -178,85 +194,6 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.AudioService.PrivateMemoryFootprint" units="MB"
-    expires_after="2023-07-16">
-  <owner>olka@chromium.org</owner>
-  <owner>fhernqvist@google.com</owner>
-  <owner>tguilbert@chromium.org</owner>
-  <owner>webrtc-audio-uma@google.com</owner>
-  <summary>
-    A rough estimate of the private memory footprint of the audio service
-    process.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This memory footprint metric cannot be compared across platforms because
-    each platform relies on platform-level APIs for accounting. As such, though
-    this attempts to measure private memory footprint as best as possible, it
-    does not measure the same thing on each platform. We have not found a good
-    way to compare any system level memory metric across platforms due to the
-    different nature of memory management on each platform.
-  </summary>
-</histogram>
-
-<histogram name="Memory.AudioService.PrivateSwapFootprint" units="MB"
-    expires_after="2023-05-14">
-  <owner>olka@chromium.org</owner>
-  <owner>guidou@chromium.org</owner>
-  <owner>fhernqvist@google.com</owner>
-  <owner>tguilbert@chromium.org</owner>
-  <owner>webrtc-audio-uma@google.com</owner>
-  <summary>
-    An amount of private memory of the audio service process placed in swap
-    (VmSwap).
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-  </summary>
-</histogram>
-
-<histogram name="Memory.AudioService.ResidentSet" units="MiB"
-    expires_after="never">
-<!-- expires-never: Generic system health metric used to diagnose various performance issues. -->
-
-  <owner>fdoray@chromium.org</owner>
-  <owner>webrtc-audio-uma@google.com</owner>
-  <summary>
-    The size of the resident memory in a audio service process. This is
-    influenced by factors we control (e.g. memory that is not accessed can be
-    swapped) and factors we don't control (e.g. an unrelated process using a lot
-    of memory can force memory in our process to be swapped). Recorded once on
-    Windows/Linux/ChromeOS/Android.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This resident memory metric cannot be compared across platforms because each
-    platform relies on platform-level APIs for accounting. As such, though this
-    attempts to measure private memory footprint as best as possible, it does
-    not measure the same thing on each platform. We have not found a good way to
-    compare any system level memory metric across platforms due to the different
-    nature of memory management on each platform.
-  </summary>
-</histogram>
-
-<histogram name="Memory.AudioService.SharedMemoryFootprint" units="MB"
-    expires_after="2023-07-16">
-  <owner>olka@chromium.org</owner>
-  <owner>guidou@chromium.org</owner>
-  <owner>fhernqvist@google.com</owner>
-  <owner>tguilbert@chromium.org</owner>
-  <owner>webrtc-audio-uma@google.com</owner>
-  <summary>
-    A rough estimate of the shared memory footprint of the audio service
-    process.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-  </summary>
-</histogram>
-
 <histogram name="Memory.BackingStore" units="units" expires_after="M85">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
@@ -1763,75 +1700,6 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.PaintPreviewCompositor.PrivateMemoryFootprint"
-    units="MB" expires_after="2023-05-27">
-  <owner>ckitagawa@chromium.org</owner>
-  <owner>fredmello@chromium.org</owner>
-  <summary>
-    A rough estimate of the private memory footprint of the paint preview
-    compositor process.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This memory footprint metric cannot be compared across platforms because
-    each platform relies on platform-level APIs for accounting. As such, though
-    this attempts to measure private memory footprint as best as possible, it
-    does not measure the same thing on each platform. We have not found a good
-    way to compare any system level memory metric across platforms due to the
-    different nature of memory management on each platform.
-  </summary>
-</histogram>
-
-<histogram name="Memory.PaintPreviewCompositor.PrivateSwapFootprint" units="MB"
-    expires_after="2023-05-27">
-  <owner>ckitagawa@chromium.org</owner>
-  <owner>fredmello@chromium.org</owner>
-  <summary>
-    An amount of private memory of the paint preview compositor process placed
-    in swap (VmSwap).
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-  </summary>
-</histogram>
-
-<histogram name="Memory.PaintPreviewCompositor.ResidentSet" units="MiB"
-    expires_after="2023-05-27">
-  <owner>ckitagawa@chromium.org</owner>
-  <owner>fredmello@chromium.org</owner>
-  <summary>
-    The size of the resident memory in a paint preview compositor process. This
-    is influenced by factors we control (e.g. memory that is not accessed can be
-    swapped) and factors we don't control (e.g. an unrelated process using a lot
-    of memory can force memory in our process to be swapped). Recorded once on
-    Windows/Linux/ChromeOS/Android.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This resident memory metric cannot be compared across platforms because each
-    platform relies on platform-level APIs for accounting. As such, though this
-    attempts to measure private memory footprint as best as possible, it does
-    not measure the same thing on each platform. We have not found a good way to
-    compare any system level memory metric across platforms due to the different
-    nature of memory management on each platform.
-  </summary>
-</histogram>
-
-<histogram name="Memory.PaintPreviewCompositor.SharedMemoryFootprint"
-    units="MB" expires_after="2023-05-27">
-  <owner>ckitagawa@chromium.org</owner>
-  <owner>fredmello@chromium.org</owner>
-  <summary>
-    A rough estimate of the shared memory footprint of the paint preview
-    compositor process.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-  </summary>
-</histogram>
-
 <histogram name="Memory.ParkableImage.DiskIsUsable.5min" enum="BooleanYesNo"
     expires_after="2022-12-25">
   <owner>thiabaud@google.com</owner>
@@ -2805,79 +2673,6 @@
   </summary>
 </histogram>
 
-<histogram name="Memory.Utility.PrivateMemoryFootprint" units="MB"
-    expires_after="never">
-<!-- expires-never: useful diagnostic metric for changes in
-     Memory.Total.PrivateMemoryFootprint, which never expires. -->
-
-  <owner>jam@chromium.org</owner>
-  <summary>
-    A rough estimate of the private memory footprint of the utility process.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This memory footprint metric cannot be compared across platforms because
-    each platform relies on platform-level APIs for accounting. As such, though
-    this attempts to measure private memory footprint as best as possible, it
-    does not measure the same thing on each platform. We have not found a good
-    way to compare any system level memory metric across platforms due to the
-    different nature of memory management on each platform.
-  </summary>
-</histogram>
-
-<histogram name="Memory.Utility.PrivateSwapFootprint" units="MB"
-    expires_after="2023-03-12">
-  <owner>jam@chromium.org</owner>
-  <summary>
-    An amount of private memory of the utility process placed in swap (VmSwap).
-    Available on Android, Linux and ChromeOS.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    Note: Histogram data may be missing for mid-2020 due to expiry.
-  </summary>
-</histogram>
-
-<histogram name="Memory.Utility.ResidentSet" units="MiB" expires_after="never">
-<!-- expires-never: Generic system health metric used to diagnose various performance issues. -->
-
-  <owner>fdoray@chromium.org</owner>
-  <summary>
-    The size of the resident memory in a utility process. This is influenced by
-    factors we control (e.g. memory that is not accessed can be swapped) and
-    factors we don't control (e.g. an unrelated process using a lot of memory
-    can force memory in our process to be swapped). Recorded on
-    Windows/Linux/ChromeOS/Android.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    This resident memory metric cannot be compared across platforms because each
-    platform relies on platform-level APIs for accounting. As such, though this
-    attempts to measure private memory footprint as best as possible, it does
-    not measure the same thing on each platform. We have not found a good way to
-    compare any system level memory metric across platforms due to the different
-    nature of memory management on each platform.
-  </summary>
-</histogram>
-
-<histogram name="Memory.Utility.SharedMemoryFootprint" units="MB"
-    expires_after="2023-01-10">
-  <owner>jam@chromium.org</owner>
-  <owner>erikchen@chromium.org</owner>
-  <owner>ssid@chromium.org</owner>
-  <summary>
-    A rough estimate of the shared memory footprint of the utility process.
-
-    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
-    Android and 30 minutes on other platforms.
-
-    Note: Histogram data may be missing between M77 to M87 due to expiry.
-  </summary>
-</histogram>
-
 <histogram name="Memory.UX.Study.1" enum="UXStudy1Arm"
     expires_after="2023-05-14">
   <owner>erikchen@chromium.org</owner>
@@ -2958,6 +2753,87 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.{UtilityProcessType}.PrivateMemoryFootprint" units="MB"
+    expires_after="never">
+<!-- expires-never: useful diagnostic metric for changes in
+     Memory.Total.PrivateMemoryFootprint, which never expires. -->
+
+  <owner>erikchen@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    A rough estimate of the private memory footprint of the {UtilityProcessType}
+    process.
+
+    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
+    Android and 30 minutes on other platforms.
+
+    This memory footprint metric cannot be compared across platforms because
+    each platform relies on platform-level APIs for accounting. As such, though
+    this attempts to measure private memory footprint as best as possible, it
+    does not measure the same thing on each platform. We have not found a good
+    way to compare any system level memory metric across platforms due to the
+    different nature of memory management on each platform.
+  </summary>
+  <token key="UtilityProcessType" variants="UtilityProcessType"/>
+</histogram>
+
+<histogram name="Memory.{UtilityProcessType}.PrivateSwapFootprint" units="MB"
+    expires_after="2023-07-16">
+  <owner>erikchen@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    An amount of private memory of the {UtilityProcessType} process placed in
+    swap (VmSwap). Available on Android, Linux and ChromeOS.
+
+    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
+    Android and 30 minutes on other platforms.
+
+    Note: Histogram data may be missing for mid-2020 due to expiry.
+  </summary>
+  <token key="UtilityProcessType" variants="UtilityProcessType"/>
+</histogram>
+
+<histogram name="Memory.{UtilityProcessType}.ResidentSet" units="MiB"
+    expires_after="never">
+<!-- expires-never: Generic system health metric used to diagnose various performance issues. -->
+
+  <owner>fdoray@chromium.org</owner>
+  <summary>
+    The size of the resident memory in a {UtilityProcessType} process. This is
+    influenced by factors we control (e.g. memory that is not accessed can be
+    swapped) and factors we don't control (e.g. an unrelated process using a lot
+    of memory can force memory in our process to be swapped). Recorded on
+    Windows/Linux/ChromeOS/Android.
+
+    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
+    Android and 30 minutes on other platforms.
+
+    This resident memory metric cannot be compared across platforms because each
+    platform relies on platform-level APIs for accounting. As such, though this
+    attempts to measure private memory footprint as best as possible, it does
+    not measure the same thing on each platform. We have not found a good way to
+    compare any system level memory metric across platforms due to the different
+    nature of memory management on each platform.
+  </summary>
+  <token key="UtilityProcessType" variants="UtilityProcessType"/>
+</histogram>
+
+<histogram name="Memory.{UtilityProcessType}.SharedMemoryFootprint" units="MB"
+    expires_after="2023-07-16">
+  <owner>erikchen@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
+  <summary>
+    A rough estimate of the shared memory footprint of the {UtilityProcessType}
+    process.
+
+    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
+    Android and 30 minutes on other platforms.
+
+    Note: Histogram data may be missing between M77 to M87 due to expiry.
+  </summary>
+  <token key="UtilityProcessType" variants="UtilityProcessType"/>
+</histogram>
+
 <histogram name="MemoryAndroid.DeviceMemoryClass" units="units"
     expires_after="2020-06-07">
   <owner>hajimehoshi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 07bf6ab..f079a74 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -2231,24 +2231,6 @@
   </summary>
 </histogram>
 
-<histogram name="ChildProcess.FieldTrials.CreateFromShmemSuccess"
-    enum="BooleanSuccess" expires_after="M72">
-  <owner>asvitkine@chromium.org</owner>
-  <summary>
-    Whether creating field trials from shared memory succeeded. Recorded by each
-    child process on process startup.
-  </summary>
-</histogram>
-
-<histogram name="ChildProcess.FieldTrials.CreateFromSwitchSuccess"
-    enum="BooleanSuccess" expires_after="M72">
-  <owner>asvitkine@chromium.org</owner>
-  <summary>
-    Whether creating field trials from --force-fieldtrials switch succeeded.
-    Recorded by each child process on process startup.
-  </summary>
-</histogram>
-
 <histogram name="ChildProcess.Killed2" enum="ProcessType2"
     expires_after="never">
 <!-- expires-never: Critical stability metrics. go/chrome-stability-metrics -->
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index db64c031..fcc9018c 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -732,6 +732,21 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.ExtensionTelemetry.FileData.CollectionDuration"
+    units="ms" expires_after="2023-07-23">
+  <owner>richche@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Extension telemetry includes file data such as file hashes for off-store
+    extension files. This histogram measures how long it takes to collect file
+    data for all the offstore extensions installed. It is emitted after each
+    pass of the collection process completes which occurs periodically (every 2
+    hours by default). The histogram is not emitted if there are no off-store
+    extensions installed or if the collection process is interrupted due to the
+    browser session ending or disabling Enhanced Safe Browsing for the profile.
+  </summary>
+</histogram>
+
 <histogram
     name="SafeBrowsing.ExtensionTelemetry.FileData.LargestFileSizeObserved"
     units="bytes" expires_after="2023-07-23">
@@ -786,6 +801,20 @@
   </summary>
 </histogram>
 
+<histogram
+    name="SafeBrowsing.ExtensionTelemetry.FileData.NumOffstoreExtensions"
+    units="extensions" expires_after="2023-07-23">
+  <owner>richche@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Extension telemetry includes file data such as file hashes for off-store
+    extension files. This histogram records the number of off-store extensions
+    installed in a user profile. It is emitted at the start of each pass of the
+    collection process which occurs periodically (every 2 hours by default). The
+    histogram is emitted even if there are no off-store extensions installed.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.ExtensionTelemetry.FileData.ProcessedFileSize"
     units="bytes" expires_after="2023-07-23">
   <owner>richche@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index a14fe737..f484d48 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -22,6 +22,49 @@
 
 <histograms>
 
+<histogram name="ChildProcess.FieldTrials.CreateFromShmemSuccess"
+    enum="BooleanSuccess" expires_after="2023-07-15">
+  <owner>asvitkine@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Whether creating field trials from shared memory succeeded. Recorded by each
+    child process on process startup.
+  </summary>
+</histogram>
+
+<histogram name="ChildProcess.FieldTrials.CreateFromSwitchSuccess"
+    enum="BooleanSuccess" expires_after="2023-07-15">
+  <owner>asvitkine@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Whether creating field trials from --force-fieldtrials switch succeeded.
+    Recorded by each child process on process startup.
+  </summary>
+</histogram>
+
+<histogram
+    name="ChildProcess.FieldTrials.GetInitiallyActiveFieldTrials.FromString"
+    enum="Boolean" expires_after="2023-07-15">
+  <owner>asvitkine@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Logged if there is no field trial allocator available when getting the list
+    of initially active field trials, resulting to a fallback to the command \
+    line. Note: False is never logged.
+  </summary>
+</histogram>
+
+<histogram name="ChildProcess.FieldTrials.PopulateLaunchOptions.CommandLine"
+    enum="Boolean" expires_after="2023-07-15">
+  <owner>asvitkine@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Logged if the command line was populated with field trial information due to
+    the read-only shared memory handle not being available, when starting a
+    child process. Note: False is never logged.
+  </summary>
+</histogram>
+
 <histogram name="Variations.AppliedSeed.Size" units="bytes"
     expires_after="2023-07-30">
   <owner>caitlinfischer@google.com</owner>
diff --git a/tools/metrics/histograms/validate_format.py b/tools/metrics/histograms/validate_format.py
index 03490aba..c4f774b5 100755
--- a/tools/metrics/histograms/validate_format.py
+++ b/tools/metrics/histograms/validate_format.py
@@ -13,9 +13,11 @@
 import histogram_paths
 import merge_xml
 
-# The allowlist of namespaces that are split across multiple files.
+# The allowlist of namespaces (histogram prefixes, case insensitive) that are
+# split across multiple files.
 _NAMESPACES_IN_MULTIPLE_FILES = [
-    'ash', 'autocomplete', 'chromeos', 'fcminvalidations', 'graphics', 'launch'
+    'ash', 'autocomplete', 'childprocess', 'chromeos', 'fcminvalidations',
+    'graphics', 'launch'
 ]
 
 
diff --git a/tools/perf/PRESUBMIT.py b/tools/perf/PRESUBMIT.py
index edf0f1d..fc1ce15b 100644
--- a/tools/perf/PRESUBMIT.py
+++ b/tools/perf/PRESUBMIT.py
@@ -178,7 +178,7 @@
       # Intentionally invalid JSON file.
       continue
     try:
-      input_api.json.load(open(filename))
+      input_api.json.load(open(filename, encoding='utf-8'))
     except ValueError:
       return [output_api.PresubmitError('Error parsing JSON in %s!' % filename)]
   return []
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index a00631b5..70f27c01 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/1d202f2412734f1e782d991618c7c4a7952e8738/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "29b1feda196dd18068e5ccab2b3a638ca9731d4c",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6a84e7b38165dd5d14af8dc4faa47cad2caf8709/trace_processor_shell.exe"
+            "hash": "89da53f44f6976e7dd87651d2a2e6f03647078b1",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/7c5fe42e47b51d5f745aa543a76b091bf3e0daa6/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "0606572451d2b1efd97a9cbc99906d2ca8351d1f",
diff --git a/tools/traffic_annotation/safe_list.txt b/tools/traffic_annotation/safe_list.txt
index 0b5816cd..eb8bcc88 100644
--- a/tools/traffic_annotation/safe_list.txt
+++ b/tools/traffic_annotation/safe_list.txt
@@ -192,8 +192,8 @@
 missing_new_fields,components/captive_portal/content/captive_portal_service.cc
 missing_new_fields,components/commerce/core/account_checker.cc
 missing_new_fields,components/commerce/core/subscriptions/subscriptions_server_proxy.cc
+missing_new_fields,components/content_relationship_verification/digital_asset_links_handler.cc
 missing_new_fields,components/contextual_search/core/browser/contextual_search_delegate_impl.cc
-missing_new_fields,components/digital_asset_links/digital_asset_links_handler.cc
 missing_new_fields,components/dom_distiller/core/distiller_url_fetcher.cc
 missing_new_fields,components/domain_reliability/uploader.cc
 missing_new_fields,components/download/internal/common/download_item_impl.cc
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index b4531924..275c548 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -61,7 +61,7 @@
  <item id="devtools_network_resource" added_in_milestone="62" content_hash_code="01f4a4af" os_list="linux,windows,chromeos" file_path="chrome/browser/devtools/devtools_ui_bindings.cc" />
  <item id="devtools_proxy_config" added_in_milestone="85" content_hash_code="01e83c36" os_list="linux,windows,chromeos,android" file_path="content/browser/devtools/protocol/target_handler.cc" />
  <item id="dial_url_fetcher" added_in_milestone="67" content_hash_code="07bd0650" os_list="linux,windows,chromeos" file_path="chrome/browser/media/router/discovery/dial/dial_url_fetcher.cc" />
- <item id="digital_asset_links" added_in_milestone="90" content_hash_code="00ced49e" os_list="linux,windows,chromeos,android" file_path="components/digital_asset_links/digital_asset_links_handler.cc" />
+ <item id="digital_asset_links" added_in_milestone="90" content_hash_code="00ced49e" os_list="linux,windows,chromeos,android" file_path="components/content_relationship_verification/digital_asset_links_handler.cc" />
  <item id="direct_sockets" added_in_milestone="88" content_hash_code="03dc0aad" os_list="linux,windows,chromeos" file_path="content/browser/direct_sockets/direct_sockets_service_impl.cc" />
  <item id="dns_over_https" added_in_milestone="66" content_hash_code="02b087b6" os_list="linux,windows,chromeos,android" file_path="net/dns/dns_transaction.cc" />
  <item id="dns_transaction" added_in_milestone="65" content_hash_code="07e14f9f" os_list="linux,windows,chromeos,android" file_path="net/dns/dns_transaction.cc" />
@@ -361,7 +361,7 @@
  <item id="printing_server_printers_query" added_in_milestone="106" content_hash_code="06f4759c" os_list="chromeos" file_path="chrome/browser/ash/printing/server_printers_fetcher.cc" />
  <item id="download_bubble_retry_download" added_in_milestone="105" content_hash_code="000b1439" os_list="linux,windows,chromeos" file_path="chrome/browser/download/bubble/download_bubble_controller.cc" />
  <item id="wilco_dtc_supportd" added_in_milestone="108" content_hash_code="01050d07" os_list="chromeos" file_path="chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_web_request_service.cc" />
- <item id="quick_answers_loader" added_in_milestone="105" content_hash_code="070b3239" os_list="chromeos" file_path="chromeos/components/quick_answers/result_loader.cc" />
+ <item id="quick_answers_loader" added_in_milestone="105" content_hash_code="06f129c2" os_list="chromeos" file_path="chromeos/components/quick_answers/result_loader.cc" />
  <item id="chrome_search_suggest_service" added_in_milestone="105" content_hash_code="04d973c6" os_list="linux,windows,android,chromeos" file_path="components/search/start_suggest_service.cc" />
  <item id="coupon_persisted_tab_data" added_in_milestone="105" content_hash_code="006dcd97" os_list="android" file_path="chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CouponPersistedTabData.java" />
  <item id="speculation_rules_prefetch_probe" added_in_milestone="105" content_hash_code="04341e21" os_list="linux,windows,android,chromeos" file_path="content/browser/preloading/prefetch/prefetch_origin_prober.cc" />
diff --git a/ui/accessibility/PRESUBMIT.py b/ui/accessibility/PRESUBMIT.py
index 16ebec1..19231ed 100644
--- a/ui/accessibility/PRESUBMIT.py
+++ b/ui/accessibility/PRESUBMIT.py
@@ -34,7 +34,7 @@
 def GetEnumsFromFile(fullpath, get_raw_enum_value=False):
   enum_name = None
   enums = {}
-  for line in open(fullpath).readlines():
+  for line in open(fullpath, encoding='utf-8').readlines():
     # Strip out comments
     line = re.sub('//.*', '', line)
 
@@ -222,7 +222,7 @@
 # header)
 def GetConstexprFromFile(fullpath):
   values = []
-  for line in open(fullpath).readlines():
+  for line in open(fullpath, encoding='utf-8').readlines():
     # Strip out comments
     line = re.sub('//.*', '', line)
 
@@ -242,7 +242,7 @@
 def GetAccessibilityModesFromFile(fullpath):
   values = []
   inside = False
-  for line in open(fullpath).readlines():
+  for line in open(fullpath, encoding='utf-8').readlines():
     if not inside:
       # Look for the block of code that defines the AXMode enum.
       m = re.search('^enum AxMode {$', line)
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index d5a30a6..41f0ff4 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -1493,6 +1493,8 @@
       return "";
     case ax::mojom::IsPopup::kAuto:
       return "auto";
+    case ax::mojom::IsPopup::kHint:
+      return "hint";
     case ax::mojom::IsPopup::kManual:
       return "manual";
   }
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 6e786a5c..8811e81 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -1176,6 +1176,7 @@
   kNone = 0,
   kManual,
   kAuto,
+  kHint,
 };
 
 enum InvalidState {
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 2cfa4d6..a428deb 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -1579,6 +1579,9 @@
           case ax::mojom::IsPopup::kAuto:
             result += " ispopup=auto";
             break;
+          case ax::mojom::IsPopup::kHint:
+            result += " ispopup=hint";
+            break;
           case ax::mojom::IsPopup::kManual:
             result += " ispopup=manual";
             break;
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index d3b8a6b5..548efed 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -1355,6 +1355,9 @@
       case ax::mojom::IsPopup::kAuto:
         AddAttributeToList("ispopup", "auto", attributes);
         break;
+      case ax::mojom::IsPopup::kHint:
+        AddAttributeToList("ispopup", "hint", attributes);
+        break;
     }
   }
 
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 66af287..a0214ec 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -368,6 +368,7 @@
   E_CPONLY(kColorToggleButtonThumbOffDisabled) \
   E_CPONLY(kColorToggleButtonThumbOn) \
   E_CPONLY(kColorToggleButtonThumbOnDisabled) \
+  E_CPONLY(kColorToggleButtonThumbOnHoverPressed) \
   E_CPONLY(kColorToggleButtonThumbOnIcon) \
   E_CPONLY(kColorToggleButtonTrackOff) \
   E_CPONLY(kColorToggleButtonTrackOn) \
diff --git a/ui/color/material_ui_color_mixer.cc b/ui/color/material_ui_color_mixer.cc
index 6ce94b9..c2defe01 100644
--- a/ui/color/material_ui_color_mixer.cc
+++ b/ui/color/material_ui_color_mixer.cc
@@ -70,6 +70,7 @@
   mixer[kColorToggleButtonThumbOffDisabled] = {kColorSysStateDisabled};
   mixer[kColorToggleButtonThumbOn] = {kColorSysOnPrimary};
   mixer[kColorToggleButtonThumbOnDisabled] = {kColorSysSurface};
+  mixer[kColorToggleButtonThumbOnHoverPressed] = {kColorSysPrimaryContainer};
   mixer[kColorToggleButtonThumbOnIcon] = {kColorSysOnPrimaryContainer};
   mixer[kColorToggleButtonTrackOff] = {kColorSysSurfaceVariant};
   mixer[kColorToggleButtonTrackOn] = {kColorSysPrimary};
diff --git a/ui/events/ozone/evdev/BUILD.gn b/ui/events/ozone/evdev/BUILD.gn
index adb9c39..31296ee 100644
--- a/ui/events/ozone/evdev/BUILD.gn
+++ b/ui/events/ozone/evdev/BUILD.gn
@@ -195,6 +195,7 @@
   visibility += [
     "//ash/webui/diagnostics_ui/*",
     "//ui/chromeos/*",
+    "//ui/events/*",
     "//ui/ozone/*",
   ]
 }
diff --git a/ui/events/ozone/evdev/input_controller_evdev.cc b/ui/events/ozone/evdev/input_controller_evdev.cc
index f9af545..085f306 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.cc
+++ b/ui/events/ozone/evdev/input_controller_evdev.cc
@@ -16,6 +16,7 @@
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/stylus_state.h"
 #include "ui/events/ozone/evdev/input_device_factory_evdev_proxy.h"
+#include "ui/events/ozone/evdev/input_device_settings_evdev.h"
 #include "ui/events/ozone/evdev/keyboard_evdev.h"
 #include "ui/events/ozone/evdev/mouse_button_map_evdev.h"
 
@@ -154,27 +155,28 @@
 }
 
 void InputControllerEvdev::SetTouchpadSensitivity(int value) {
-  input_device_settings_.touchpad_sensitivity = value;
+  input_device_settings_.GetTouchpadSettings().sensitivity = value;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTouchpadScrollSensitivity(int value) {
-  input_device_settings_.touchpad_scroll_sensitivity = value;
+  input_device_settings_.GetTouchpadSettings().scroll_sensitivity = value;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTouchpadHapticFeedback(bool enabled) {
-  input_device_settings_.touchpad_haptic_feedback_enabled = enabled;
+  input_device_settings_.GetTouchpadSettings().haptic_feedback_enabled =
+      enabled;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTouchpadHapticClickSensitivity(int value) {
-  input_device_settings_.touchpad_haptic_click_sensitivity = value;
+  input_device_settings_.GetTouchpadSettings().haptic_click_sensitivity = value;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTapToClick(bool enabled) {
-  input_device_settings_.tap_to_click_enabled = enabled;
+  input_device_settings_.GetTouchpadSettings().tap_to_click_enabled = enabled;
   ScheduleUpdateDeviceSettings();
 }
 
@@ -184,27 +186,27 @@
 }
 
 void InputControllerEvdev::SetTapDragging(bool enabled) {
-  input_device_settings_.tap_dragging_enabled = enabled;
+  input_device_settings_.GetTouchpadSettings().tap_dragging_enabled = enabled;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetNaturalScroll(bool enabled) {
-  input_device_settings_.natural_scroll_enabled = enabled;
+  input_device_settings_.GetTouchpadSettings().natural_scroll_enabled = enabled;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetMouseSensitivity(int value) {
-  input_device_settings_.mouse_sensitivity = value;
+  input_device_settings_.GetMouseSettings().sensitivity = value;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetMouseScrollSensitivity(int value) {
-  input_device_settings_.mouse_scroll_sensitivity = value;
+  input_device_settings_.GetMouseSettings().scroll_sensitivity = value;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetPointingStickSensitivity(int value) {
-  input_device_settings_.pointing_stick_sensitivity = value;
+  input_device_settings_.GetPointingStickSettings().sensitivity = value;
   ScheduleUpdateDeviceSettings();
 }
 
@@ -213,7 +215,8 @@
     stored_acceleration_settings_->pointing_stick = enabled;
     return;
   }
-  input_device_settings_.pointing_stick_acceleration_enabled = enabled;
+  input_device_settings_.GetPointingStickSettings().acceleration_enabled =
+      enabled;
   ScheduleUpdateDeviceSettings();
 }
 
@@ -238,7 +241,7 @@
 }
 
 void InputControllerEvdev::SetMouseReverseScroll(bool enabled) {
-  input_device_settings_.mouse_reverse_scroll_enabled = enabled;
+  input_device_settings_.GetMouseSettings().reverse_scroll_enabled = enabled;
   ScheduleUpdateDeviceSettings();
 }
 
@@ -247,7 +250,7 @@
     stored_acceleration_settings_->mouse = enabled;
     return;
   }
-  input_device_settings_.mouse_acceleration_enabled = enabled;
+  input_device_settings_.GetMouseSettings().acceleration_enabled = enabled;
   ScheduleUpdateDeviceSettings();
 }
 
@@ -257,11 +260,12 @@
   stored_acceleration_settings_ =
       std::make_unique<StoredAccelerationSettings>();
   stored_acceleration_settings_->mouse =
-      input_device_settings_.mouse_acceleration_enabled;
+      input_device_settings_.GetMouseSettings().acceleration_enabled;
   stored_acceleration_settings_->pointing_stick =
-      input_device_settings_.pointing_stick_acceleration_enabled;
-  input_device_settings_.mouse_acceleration_enabled = false;
-  input_device_settings_.pointing_stick_acceleration_enabled = false;
+      input_device_settings_.GetPointingStickSettings().acceleration_enabled;
+  input_device_settings_.GetMouseSettings().acceleration_enabled = false;
+  input_device_settings_.GetPointingStickSettings().acceleration_enabled =
+      false;
   ScheduleUpdateDeviceSettings();
 }
 
@@ -272,17 +276,19 @@
 }
 
 void InputControllerEvdev::SetMouseScrollAcceleration(bool enabled) {
-  input_device_settings_.mouse_scroll_acceleration_enabled = enabled;
+  input_device_settings_.GetMouseSettings().scroll_acceleration_enabled =
+      enabled;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTouchpadAcceleration(bool enabled) {
-  input_device_settings_.touchpad_acceleration_enabled = enabled;
+  input_device_settings_.GetTouchpadSettings().acceleration_enabled = enabled;
   ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTouchpadScrollAcceleration(bool enabled) {
-  input_device_settings_.touchpad_scroll_acceleration_enabled = enabled;
+  input_device_settings_.GetTouchpadSettings().scroll_acceleration_enabled =
+      enabled;
   ScheduleUpdateDeviceSettings();
 }
 
diff --git a/ui/events/ozone/evdev/input_controller_evdev_unittest.cc b/ui/events/ozone/evdev/input_controller_evdev_unittest.cc
index b3e8e4ad..640da90 100644
--- a/ui/events/ozone/evdev/input_controller_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/input_controller_evdev_unittest.cc
@@ -5,6 +5,7 @@
 #include "ui/events/ozone/evdev/input_controller_evdev.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/ozone/evdev/input_device_settings_evdev.h"
 
 namespace ui {
 
@@ -13,21 +14,24 @@
   controller.SetMouseAcceleration(true);
   controller.SetPointingStickAcceleration(true);
 
-  EXPECT_TRUE(controller.input_device_settings_.mouse_acceleration_enabled);
-  EXPECT_TRUE(
-      controller.input_device_settings_.pointing_stick_acceleration_enabled);
+  EXPECT_TRUE(controller.input_device_settings_.GetMouseSettings()
+                  .acceleration_enabled);
+  EXPECT_TRUE(controller.input_device_settings_.GetPointingStickSettings()
+                  .acceleration_enabled);
 
   // Suspending should disable the acceleration temporarily.
   controller.SuspendMouseAcceleration();
-  EXPECT_FALSE(controller.input_device_settings_.mouse_acceleration_enabled);
-  EXPECT_FALSE(
-      controller.input_device_settings_.pointing_stick_acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetMouseSettings()
+                   .acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetPointingStickSettings()
+                   .acceleration_enabled);
 
   // Resuming should enable it again.
   controller.EndMouseAccelerationSuspension();
-  EXPECT_TRUE(controller.input_device_settings_.mouse_acceleration_enabled);
-  EXPECT_TRUE(
-      controller.input_device_settings_.pointing_stick_acceleration_enabled);
+  EXPECT_TRUE(controller.input_device_settings_.GetMouseSettings()
+                  .acceleration_enabled);
+  EXPECT_TRUE(controller.input_device_settings_.GetPointingStickSettings()
+                  .acceleration_enabled);
 }
 
 TEST(InputControllerEvdevTest, AccelerationChangeDuringSuspension) {
@@ -37,24 +41,27 @@
 
   // Suspending should disable the acceleration temporarily.
   controller.SuspendMouseAcceleration();
-  EXPECT_FALSE(controller.input_device_settings_.mouse_acceleration_enabled);
-  EXPECT_FALSE(
-      controller.input_device_settings_.pointing_stick_acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetMouseSettings()
+                   .acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetPointingStickSettings()
+                   .acceleration_enabled);
 
   // Settings changes while suspended should not take effect immediately...
   controller.SetMouseAcceleration(true);
   controller.SetPointingStickAcceleration(true);
-  EXPECT_FALSE(controller.input_device_settings_.mouse_acceleration_enabled);
-  EXPECT_FALSE(
-      controller.input_device_settings_.pointing_stick_acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetMouseSettings()
+                   .acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetPointingStickSettings()
+                   .acceleration_enabled);
 
   // ...instead being applied when the suspension ends.
   controller.SetMouseAcceleration(false);
   controller.SetPointingStickAcceleration(false);
   controller.EndMouseAccelerationSuspension();
-  EXPECT_FALSE(controller.input_device_settings_.mouse_acceleration_enabled);
-  EXPECT_FALSE(
-      controller.input_device_settings_.pointing_stick_acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetMouseSettings()
+                   .acceleration_enabled);
+  EXPECT_FALSE(controller.input_device_settings_.GetPointingStickSettings()
+                   .acceleration_enabled);
 }
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc
index eb83a5d..bc2b5ab 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -26,6 +26,7 @@
 #include "ui/events/ozone/evdev/event_converter_evdev_impl.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
 #include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h"
+#include "ui/events/ozone/evdev/input_device_settings_evdev.h"
 #include "ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h"
 #include "ui/events/ozone/evdev/microphone_mute_switch_event_converter_evdev.h"
 #include "ui/events/ozone/evdev/stylus_button_event_converter_evdev.h"
@@ -300,38 +301,46 @@
 
   SetIntPropertyForOneType(
       DT_TOUCHPAD, "Haptic Button Sensitivity",
-      input_device_settings_.touchpad_haptic_click_sensitivity);
-  SetIntPropertyForOneType(DT_TOUCHPAD, "Pointer Sensitivity",
-                           input_device_settings_.touchpad_sensitivity);
-  SetIntPropertyForOneType(DT_TOUCHPAD, "Scroll Sensitivity",
-                           input_device_settings_.touchpad_scroll_sensitivity);
+      input_device_settings_.GetTouchpadSettings().haptic_click_sensitivity);
+  SetIntPropertyForOneType(
+      DT_TOUCHPAD, "Pointer Sensitivity",
+      input_device_settings_.GetTouchpadSettings().sensitivity);
+  SetIntPropertyForOneType(
+      DT_TOUCHPAD, "Scroll Sensitivity",
+      input_device_settings_.GetTouchpadSettings().scroll_sensitivity);
   SetBoolPropertyForOneType(
       DT_TOUCHPAD, "Pointer Acceleration",
-      input_device_settings_.touchpad_acceleration_enabled);
+      input_device_settings_.GetTouchpadSettings().acceleration_enabled);
   SetBoolPropertyForOneType(
       DT_TOUCHPAD, "Scroll Acceleration",
-      input_device_settings_.touchpad_scroll_acceleration_enabled);
+      input_device_settings_.GetTouchpadSettings().scroll_acceleration_enabled);
 
-  SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Enable",
-                            input_device_settings_.tap_to_click_enabled);
+  SetBoolPropertyForOneType(
+      DT_TOUCHPAD, "Tap Enable",
+      input_device_settings_.GetTouchpadSettings().tap_to_click_enabled);
   SetBoolPropertyForOneType(DT_TOUCHPAD, "T5R2 Three Finger Click Enable",
                             input_device_settings_.three_finger_click_enabled);
-  SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Drag Enable",
-                            input_device_settings_.tap_dragging_enabled);
+  SetBoolPropertyForOneType(
+      DT_TOUCHPAD, "Tap Drag Enable",
+      input_device_settings_.GetTouchpadSettings().tap_dragging_enabled);
 
-  SetBoolPropertyForOneType(DT_MULTITOUCH, "Australian Scrolling",
-                            input_device_settings_.natural_scroll_enabled);
+  SetBoolPropertyForOneType(
+      DT_MULTITOUCH, "Australian Scrolling",
+      input_device_settings_.GetTouchpadSettings().natural_scroll_enabled);
 
-  SetIntPropertyForOneType(DT_MOUSE, "Pointer Sensitivity",
-                           input_device_settings_.mouse_sensitivity);
-  SetBoolPropertyForOneType(DT_MOUSE, "Pointer Acceleration",
-                            input_device_settings_.mouse_acceleration_enabled);
+  SetIntPropertyForOneType(
+      DT_MOUSE, "Pointer Sensitivity",
+      input_device_settings_.GetMouseSettings().sensitivity);
+  SetBoolPropertyForOneType(
+      DT_MOUSE, "Pointer Acceleration",
+      input_device_settings_.GetMouseSettings().acceleration_enabled);
   ApplyRelativePointingDeviceSettings(DT_MOUSE);
-  SetIntPropertyForOneType(DT_POINTING_STICK, "Pointer Sensitivity",
-                           input_device_settings_.pointing_stick_sensitivity);
+  SetIntPropertyForOneType(
+      DT_POINTING_STICK, "Pointer Sensitivity",
+      input_device_settings_.GetPointingStickSettings().sensitivity);
   SetBoolPropertyForOneType(
       DT_POINTING_STICK, "Pointer Acceleration",
-      input_device_settings_.pointing_stick_acceleration_enabled);
+      input_device_settings_.GetPointingStickSettings().acceleration_enabled);
   ApplyRelativePointingDeviceSettings(DT_POINTING_STICK);
 
   SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused",
@@ -372,14 +381,17 @@
 
 void InputDeviceFactoryEvdev::ApplyRelativePointingDeviceSettings(
     EventDeviceType type) {
-  SetIntPropertyForOneType(type, "Mouse Scroll Sensitivity",
-                           input_device_settings_.mouse_scroll_sensitivity);
-  SetBoolPropertyForOneType(
-      type, "Mouse Scroll Acceleration",
-      input_device_settings_.mouse_scroll_acceleration_enabled);
-  SetBoolPropertyForOneType(
-      type, "Mouse Reverse Scrolling",
-      input_device_settings_.mouse_reverse_scroll_enabled);
+  if (type == DT_MOUSE) {
+    SetIntPropertyForOneType(
+        type, "Mouse Scroll Sensitivity",
+        input_device_settings_.GetMouseSettings().scroll_sensitivity);
+    SetBoolPropertyForOneType(
+        type, "Mouse Scroll Acceleration",
+        input_device_settings_.GetMouseSettings().scroll_acceleration_enabled);
+    SetBoolPropertyForOneType(
+        type, "Mouse Reverse Scrolling",
+        input_device_settings_.GetMouseSettings().reverse_scroll_enabled);
+  }
   SetBoolPropertyForOneType(type, "Mouse High Resolution Scrolling", true);
   SetBoolPropertyForOneType(type, "Output Mouse Wheel Gestures", true);
 }
diff --git a/ui/events/ozone/evdev/input_device_settings_evdev.cc b/ui/events/ozone/evdev/input_device_settings_evdev.cc
index fb87990c..fe554b5 100644
--- a/ui/events/ozone/evdev/input_device_settings_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_settings_evdev.cc
@@ -8,16 +8,98 @@
 #include "ui/events/ozone/features.h"
 
 namespace ui {
+namespace {
+// Used to denote the global instance of settings within the maps which is used
+// when per device settings are disabled.
+constexpr int kSharedSettingsDeviceId = -1;
+}  // namespace
 
 InputDeviceSettingsEvdev::InputDeviceSettingsEvdev() {
   touch_event_logging_enabled =
       base::FeatureList::IsEnabled(ui::kEnableInputEventLogging);
 }
-
 InputDeviceSettingsEvdev::InputDeviceSettingsEvdev(
-    const InputDeviceSettingsEvdev& other) = default;
+    const InputDeviceSettingsEvdev& input_device_settings) = default;
+InputDeviceSettingsEvdev::~InputDeviceSettingsEvdev() = default;
 
-InputDeviceSettingsEvdev::~InputDeviceSettingsEvdev() {
+TouchpadSettingsEvdev& InputDeviceSettingsEvdev::GetTouchpadSettings() {
+  return touchpad_settings_[kSharedSettingsDeviceId];
 }
 
+MouseSettingsEvdev& InputDeviceSettingsEvdev::GetMouseSettings() {
+  return mouse_settings_[kSharedSettingsDeviceId];
+}
+
+PointingStickSettingsEvdev&
+InputDeviceSettingsEvdev::GetPointingStickSettings() {
+  return pointing_stick_settings_[kSharedSettingsDeviceId];
+}
+
+const TouchpadSettingsEvdev& InputDeviceSettingsEvdev::GetTouchpadSettings()
+    const {
+  return touchpad_settings_.at(kSharedSettingsDeviceId);
+}
+
+const MouseSettingsEvdev& InputDeviceSettingsEvdev::GetMouseSettings() const {
+  return mouse_settings_.at(kSharedSettingsDeviceId);
+}
+
+const PointingStickSettingsEvdev&
+InputDeviceSettingsEvdev::GetPointingStickSettings() const {
+  return pointing_stick_settings_.at(kSharedSettingsDeviceId);
+}
+
+TouchpadSettingsEvdev& InputDeviceSettingsEvdev::GetTouchpadSettings(
+    int device_id) {
+  if (!enable_per_device_settings) {
+    return GetTouchpadSettings();
+  }
+  return touchpad_settings_[device_id];
+}
+
+MouseSettingsEvdev& InputDeviceSettingsEvdev::GetMouseSettings(int device_id) {
+  if (!enable_per_device_settings) {
+    return GetMouseSettings();
+  }
+
+  return mouse_settings_[device_id];
+}
+
+PointingStickSettingsEvdev& InputDeviceSettingsEvdev::GetPointingStickSettings(
+    int device_id) {
+  if (!enable_per_device_settings) {
+    return GetPointingStickSettings();
+  }
+  return pointing_stick_settings_[device_id];
+}
+
+const TouchpadSettingsEvdev& InputDeviceSettingsEvdev::GetTouchpadSettings(
+    int device_id) const {
+  if (!enable_per_device_settings) {
+    return GetTouchpadSettings();
+  }
+  return touchpad_settings_.at(device_id);
+}
+
+const MouseSettingsEvdev& InputDeviceSettingsEvdev::GetMouseSettings(
+    int device_id) const {
+  if (!enable_per_device_settings) {
+    return GetMouseSettings();
+  }
+  return mouse_settings_.at(device_id);
+}
+
+const PointingStickSettingsEvdev&
+InputDeviceSettingsEvdev::GetPointingStickSettings(int device_id) const {
+  if (!enable_per_device_settings) {
+    return GetPointingStickSettings();
+  }
+  return pointing_stick_settings_.at(device_id);
+}
+
+TouchpadSettingsEvdev::TouchpadSettingsEvdev() = default;
+TouchpadSettingsEvdev::TouchpadSettingsEvdev(
+    const TouchpadSettingsEvdev& touchpad_settings_) = default;
+TouchpadSettingsEvdev::~TouchpadSettingsEvdev() = default;
+
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/input_device_settings_evdev.h b/ui/events/ozone/evdev/input_device_settings_evdev.h
index cf8a0a8..55c5fdf 100644
--- a/ui/events/ozone/evdev/input_device_settings_evdev.h
+++ b/ui/events/ozone/evdev/input_device_settings_evdev.h
@@ -7,45 +7,87 @@
 
 #include <vector>
 
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
+
 namespace ui {
 
 enum class DomCode;
 
-struct InputDeviceSettingsEvdev {
-  InputDeviceSettingsEvdev();
-  InputDeviceSettingsEvdev(const InputDeviceSettingsEvdev& other);
-  ~InputDeviceSettingsEvdev();
+constexpr int kDefaultSensitivity = 3;
 
-  static const int kDefaultSensitivity = 3;
+struct MouseSettingsEvdev {
+  // The initial settings are not critical since they will be shortly be changed
+  // to the user's preferences or the application's own defaults.
+  bool reverse_scroll_enabled = false;
+  bool acceleration_enabled = true;
+  bool scroll_acceleration_enabled = true;
+  int sensitivity = kDefaultSensitivity;
+  int scroll_sensitivity = kDefaultSensitivity;
+};
+
+struct TouchpadSettingsEvdev {
+  TouchpadSettingsEvdev();
+  TouchpadSettingsEvdev(const TouchpadSettingsEvdev&);
+  ~TouchpadSettingsEvdev();
 
   // The initial settings are not critical since they will be shortly be changed
   // to the user's preferences or the application's own defaults.
   bool tap_to_click_enabled = true;
-  bool three_finger_click_enabled = false;
   bool tap_dragging_enabled = false;
   bool natural_scroll_enabled = false;
+  bool acceleration_enabled = true;
+  bool scroll_acceleration_enabled = true;
+  bool haptic_feedback_enabled = true;
+  int sensitivity = kDefaultSensitivity;
+  int scroll_sensitivity = kDefaultSensitivity;
+  int haptic_click_sensitivity = kDefaultSensitivity;
+};
+
+struct PointingStickSettingsEvdev {
+  // The initial settings are not critical since they will be shortly be changed
+  // to the user's preferences or the application's own defaults.
+  bool acceleration_enabled = true;
+  int sensitivity = kDefaultSensitivity;
+};
+
+struct COMPONENT_EXPORT(EVDEV) InputDeviceSettingsEvdev {
+  InputDeviceSettingsEvdev();
+  InputDeviceSettingsEvdev(const InputDeviceSettingsEvdev&);
+  ~InputDeviceSettingsEvdev();
+
+  TouchpadSettingsEvdev& GetTouchpadSettings();
+  TouchpadSettingsEvdev& GetTouchpadSettings(int device_id);
+  const TouchpadSettingsEvdev& GetTouchpadSettings() const;
+  const TouchpadSettingsEvdev& GetTouchpadSettings(int device_id) const;
+
+  MouseSettingsEvdev& GetMouseSettings();
+  MouseSettingsEvdev& GetMouseSettings(int device_id);
+  const MouseSettingsEvdev& GetMouseSettings() const;
+  const MouseSettingsEvdev& GetMouseSettings(int device_id) const;
+
+  PointingStickSettingsEvdev& GetPointingStickSettings();
+  PointingStickSettingsEvdev& GetPointingStickSettings(int device_id);
+  const PointingStickSettingsEvdev& GetPointingStickSettings() const;
+  const PointingStickSettingsEvdev& GetPointingStickSettings(
+      int device_id) const;
+
+  bool enable_per_device_settings = false;
+  // Pausing of tap to click applies to all touchpad devices.
   bool tap_to_click_paused = false;
+  // Three finger click applies to all touchpad devices.
+  bool three_finger_click_enabled = false;
   bool touch_event_logging_enabled = false;
-  bool mouse_reverse_scroll_enabled = false;
-  bool mouse_acceleration_enabled = true;
-  bool mouse_scroll_acceleration_enabled = true;
-  bool pointing_stick_acceleration_enabled = true;
-  bool touchpad_acceleration_enabled = true;
-  bool touchpad_scroll_acceleration_enabled = true;
-  bool touchpad_haptic_feedback_enabled = true;
-
-  int touchpad_sensitivity = kDefaultSensitivity;
-  int touchpad_scroll_sensitivity = kDefaultSensitivity;
-  int touchpad_haptic_click_sensitivity = kDefaultSensitivity;
-  int mouse_sensitivity = kDefaultSensitivity;
-  int mouse_scroll_sensitivity = kDefaultSensitivity;
-  int pointing_stick_sensitivity = kDefaultSensitivity;
-
   bool enable_devices = true;  // If false, all input is disabled.
   bool enable_internal_touchpad = true;
   bool enable_touch_screens = true;
   bool enable_internal_keyboard_filter = false;
   std::vector<DomCode> internal_keyboard_allowed_keys;
+
+ private:
+  base::flat_map<int, TouchpadSettingsEvdev> touchpad_settings_;
+  base::flat_map<int, MouseSettingsEvdev> mouse_settings_;
+  base::flat_map<int, PointingStickSettingsEvdev> pointing_stick_settings_;
 };
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc b/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc
index a6e9ea2a..c41cad1 100644
--- a/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc
+++ b/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc
@@ -137,12 +137,13 @@
 
 void EventReaderLibevdevCros::ApplyDeviceSettings(
     const InputDeviceSettingsEvdev& settings) {
+  const auto& touchpad_settings = settings.GetTouchpadSettings();
   if (haptic_touchpad_handler_) {
     haptic_touchpad_handler_->SetClickStrength(
         static_cast<HapticTouchpadEffectStrength>(
-            settings.touchpad_haptic_click_sensitivity));
+            touchpad_settings.haptic_click_sensitivity));
   }
-  haptic_feedback_enabled_ = settings.touchpad_haptic_feedback_enabled;
+  haptic_feedback_enabled_ = touchpad_settings.haptic_feedback_enabled;
 }
 
 bool EventReaderLibevdevCros::HasCapsLockLed() const {
diff --git a/ui/events/ozone/evdev/libinput_event_converter.cc b/ui/events/ozone/evdev/libinput_event_converter.cc
index 4cea9c5..00e20bd 100644
--- a/ui/events/ozone/evdev/libinput_event_converter.cc
+++ b/ui/events/ozone/evdev/libinput_event_converter.cc
@@ -111,9 +111,10 @@
 
 void LibInputEventConverter::LibInputDevice::ApplySettings(
     const InputDeviceSettingsEvdev& settings) const {
-  SetNaturalScrollEnabled(settings.natural_scroll_enabled);
-  SetSensitivity(settings.touchpad_sensitivity);
-  SetTapToClickEnabled(settings.tap_to_click_enabled);
+  const auto& touchpad_settings = settings.GetTouchpadSettings();
+  SetNaturalScrollEnabled(touchpad_settings.natural_scroll_enabled);
+  SetSensitivity(touchpad_settings.sensitivity);
+  SetTapToClickEnabled(touchpad_settings.tap_to_click_enabled);
 }
 
 // Get a comma-separated string of the device's capabilities
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 41d149e..95338b1d 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -200,6 +200,8 @@
     "gpu/drm_overlay_validator_unittest.cc",
     "gpu/drm_thread_unittest.cc",
     "gpu/drm_window_unittest.cc",
+    "gpu/fake_drm_device_generator.cc",
+    "gpu/fake_drm_device_generator.h",
     "gpu/hardware_display_controller_unittest.cc",
     "gpu/hardware_display_plane_manager_unittest.cc",
     "gpu/mock_drm_device.cc",
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_unittest.cc b/ui/ozone/platform/drm/gpu/drm_thread_unittest.cc
index df68442..e54d554 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread_unittest.cc
@@ -9,24 +9,11 @@
 #include "base/test/task_environment.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/linux/test/mock_gbm_device.h"
-#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
-#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-
+#include "ui/ozone/platform/drm/gpu/fake_drm_device_generator.h"
 namespace ui {
 
 namespace {
 
-class FakeDrmDeviceGenerator : public DrmDeviceGenerator {
-  // DrmDeviceGenerator:
-  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
-                                        base::ScopedFD fd,
-                                        bool is_primary_device) override {
-    auto gbm_device = std::make_unique<MockGbmDevice>();
-    return base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device));
-  }
-};
-
 void StubTask() {}
 
 void StubTaskWithDoneFeedback(bool* done) {
diff --git a/ui/ozone/platform/drm/gpu/fake_drm_device_generator.cc b/ui/ozone/platform/drm/gpu/fake_drm_device_generator.cc
new file mode 100644
index 0000000..f7d8f16
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/fake_drm_device_generator.cc
@@ -0,0 +1,23 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/fake_drm_device_generator.h"
+
+#include "ui/gfx/linux/test/mock_gbm_device.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
+
+namespace ui {
+scoped_refptr<DrmDevice> FakeDrmDeviceGenerator::CreateDevice(
+    const base::FilePath& path,
+    base::ScopedFD fd,
+    bool is_primary_device) {
+  auto gbm_device = std::make_unique<MockGbmDevice>();
+  if (path.empty())
+    return base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device));
+
+  return base::MakeRefCounted<MockDrmDevice>(
+      std::move(path), std::move(gbm_device), is_primary_device);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/fake_drm_device_generator.h b/ui/ozone/platform/drm/gpu/fake_drm_device_generator.h
new file mode 100644
index 0000000..f383fdf
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/fake_drm_device_generator.h
@@ -0,0 +1,27 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_FAKE_DRM_DEVICE_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_FAKE_DRM_DEVICE_GENERATOR_H_
+
+#include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_refptr.h"
+
+namespace ui {
+
+class DrmDevice;
+
+class FakeDrmDeviceGenerator : public DrmDeviceGenerator {
+  // DrmDeviceGenerator:
+  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
+                                        base::ScopedFD fd,
+                                        bool is_primary_device) override;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_FAKE_DRM_DEVICE_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
index d6996ba..120a4c62 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
@@ -48,6 +48,8 @@
 // pageflip, or other atomic property changes that do not require modesetting.
 constexpr uint32_t kSeamlessModesetFlags = 0;
 
+const std::vector<uint32_t> kBlobProperyIds = {kEdidBlobPropId};
+
 const std::map<uint32_t, std::string> kCrtcRequiredPropertyNames = {
     {kActivePropId, "ACTIVE"},
     {kModePropId, "MODE_ID"},
@@ -67,6 +69,7 @@
 const std::map<uint32_t, std::string> kConnectorRequiredPropertyNames = {
     {kCrtcIdPropId, "CRTC_ID"},
     {kLinkStatusPropId, "link-status"},
+    {kEdidBlobPropId, "EDID"},
 };
 
 const std::map<uint32_t, std::string> kPlaneRequiredPropertyNames = {
@@ -137,6 +140,10 @@
   return ++value_generator;
 }
 
+bool IsPropertyValueBlob(uint32_t prop_id) {
+  return base::Contains(kBlobProperyIds, prop_id);
+}
+
 }  // namespace
 
 MockDrmDevice::CrtcProperties::CrtcProperties() = default;
@@ -148,6 +155,11 @@
     const ConnectorProperties&) = default;
 MockDrmDevice::ConnectorProperties::~ConnectorProperties() = default;
 
+MockDrmDevice::EncoderProperties::EncoderProperties() = default;
+MockDrmDevice::EncoderProperties::EncoderProperties(const EncoderProperties&) =
+    default;
+MockDrmDevice::EncoderProperties::~EncoderProperties() = default;
+
 MockDrmDevice::PlaneProperties::PlaneProperties() = default;
 MockDrmDevice::PlaneProperties::PlaneProperties(const PlaneProperties&) =
     default;
@@ -232,12 +244,10 @@
   return state;
 }
 
-std::pair<MockDrmDevice::CrtcProperties&, MockDrmDevice::ConnectorProperties&>
-MockDrmDevice::MockDrmState::AddCrtcAndConnector() {
+MockDrmDevice::ConnectorProperties&
+MockDrmDevice::MockDrmState::AddConnector() {
   uint32_t next_connector_id =
       GetNextId(connector_properties, kConnectorIdBase);
-  uint32_t next_crtc_id = GetNextId(crtc_properties, kCrtcIdBase);
-
   auto& connector_property = connector_properties.emplace_back();
   connector_property.id = next_connector_id;
   for (const auto& pair : kConnectorRequiredPropertyNames) {
@@ -246,6 +256,19 @@
       property_names.emplace(pair.first, pair.second);
   }
 
+  return {connector_property};
+}
+
+MockDrmDevice::EncoderProperties& MockDrmDevice::MockDrmState::AddEncoder() {
+  uint32_t next_encoder_id = GetNextId(crtc_properties, kEncoderIdBase);
+  auto& encoder_property = encoder_properties.emplace_back();
+  encoder_property.id = next_encoder_id;
+
+  return {encoder_property};
+}
+
+MockDrmDevice::CrtcProperties& MockDrmDevice::MockDrmState::AddCrtc() {
+  uint32_t next_crtc_id = GetNextId(crtc_properties, kCrtcIdBase);
   auto& crtc_property = crtc_properties.emplace_back();
   crtc_property.id = next_crtc_id;
   for (const auto& pair : kCrtcRequiredPropertyNames) {
@@ -254,7 +277,12 @@
       property_names.emplace(pair.first, pair.second);
   }
 
-  return {crtc_property, connector_property};
+  return {crtc_property};
+}
+
+std::pair<MockDrmDevice::CrtcProperties&, MockDrmDevice::ConnectorProperties&>
+MockDrmDevice::MockDrmState::AddCrtcAndConnector() {
+  return {AddCrtc(), AddConnector()};
 }
 
 MockDrmDevice::PlaneProperties& MockDrmDevice::MockDrmState::AddPlane(
@@ -295,18 +323,17 @@
     : DrmDevice(base::FilePath(),
                 base::ScopedFD(),
                 true /* is_primary_device */,
-                std::move(gbm_device)),
-      set_crtc_call_count_(0),
-      add_framebuffer_call_count_(0),
-      remove_framebuffer_call_count_(0),
-      page_flip_call_count_(0),
-      overlay_clear_call_count_(0),
-      allocate_buffer_count_(0),
-      set_crtc_expectation_(true),
-      add_framebuffer_expectation_(true),
-      page_flip_expectation_(true),
-      create_dumb_buffer_expectation_(true),
-      current_framebuffer_(0) {
+                std::move(gbm_device)) {
+  plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerLegacy>(this);
+}
+
+MockDrmDevice::MockDrmDevice(const base::FilePath& path,
+                             std::unique_ptr<GbmDevice> gbm_device,
+                             bool is_primary_device)
+    : DrmDevice(std::move(path),
+                base::ScopedFD(),
+                is_primary_device,
+                std::move(gbm_device)) {
   plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerLegacy>(this);
 }
 
@@ -340,24 +367,48 @@
   return blob;
 }
 
-void MockDrmDevice::InitializeState(const MockDrmState& state,
-                                    bool use_atomic) {
+void MockDrmDevice::InitializeState(MockDrmState& state, bool use_atomic) {
   CHECK(InitializeStateWithResult(state, use_atomic));
 }
 
-bool MockDrmDevice::InitializeStateWithResult(const MockDrmState& state,
+bool MockDrmDevice::InitializeStateWithResult(MockDrmState& state,
                                               bool use_atomic) {
-  UpdateStateBesidesPlaneManager(state);
-
   if (use_atomic) {
     plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerAtomic>(this);
   } else {
     plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerLegacy>(this);
   }
 
+  MaybeSetEdidBlobsForConnectors(state);
+  UpdateStateBesidesPlaneManager(state);
+
   return plane_manager_->Initialize();
 }
 
+void MockDrmDevice::MaybeSetEdidBlobsForConnectors(MockDrmState& state) {
+  for (auto& mock_connector : state.connector_properties) {
+    const std::vector<uint8_t> edid_blob = mock_connector.edid_blob;
+    if (edid_blob.empty()) {
+      continue;
+    }
+
+    DrmWrapper::Property* mock_blob_prop =
+        FindObjectById(kEdidBlobPropId, mock_connector.properties);
+    DCHECK(mock_blob_prop);
+    // Update the mock EDID property's value to the EDID blob's ID.
+    mock_blob_prop->value = GetNextId(state.blobs, kBaseBlobId);
+    state.blobs.push_back(*mock_blob_prop);
+
+    ScopedDrmPropertyBlobPtr drm_prop_blob(
+        DrmAllocator<drmModePropertyBlobRes>());
+    drm_prop_blob->id = mock_blob_prop->value;
+    drm_prop_blob->length = mock_connector.edid_blob.size();
+    drm_prop_blob->data = drmMalloc(drm_prop_blob->length);
+    memcpy(drm_prop_blob->data, edid_blob.data(), edid_blob.size());
+    SetPropertyBlob(std::move(drm_prop_blob));
+  }
+}
+
 void MockDrmDevice::UpdateStateBesidesPlaneManager(const MockDrmState& state) {
   drm_state_ = state;
 }
@@ -424,7 +475,15 @@
 }
 
 ScopedDrmCrtcPtr MockDrmDevice::GetCrtc(uint32_t crtc_id) const {
-  return ScopedDrmCrtcPtr(DrmAllocator<drmModeCrtc>());
+  const CrtcProperties* mock_crtc =
+      FindObjectById(crtc_id, drm_state_.crtc_properties);
+  if (!mock_crtc)
+    return nullptr;
+
+  ScopedDrmCrtcPtr crtc(DrmAllocator<drmModeCrtc>());
+  crtc->crtc_id = mock_crtc->id;
+
+  return crtc;
 }
 
 bool MockDrmDevice::SetCrtc(uint32_t crtc_id,
@@ -443,7 +502,59 @@
 }
 
 ScopedDrmConnectorPtr MockDrmDevice::GetConnector(uint32_t connector_id) const {
-  return ScopedDrmConnectorPtr(DrmAllocator<drmModeConnector>());
+  const ConnectorProperties* mock_connector =
+      FindObjectById(connector_id, drm_state_.connector_properties);
+  if (!mock_connector)
+    return nullptr;
+
+  ScopedDrmConnectorPtr connector(DrmAllocator<drmModeConnector>());
+  connector->connector_id = mock_connector->id;
+  connector->connection =
+      mock_connector->connection ? DRM_MODE_CONNECTED : DRM_MODE_DISCONNECTED;
+
+  // Copy props.
+  const uint32_t count_props = mock_connector->properties.size();
+  connector->count_props = count_props;
+  connector->props = DrmAllocator<uint32_t>(count_props);
+  connector->prop_values = DrmAllocator<uint64_t>(count_props);
+  for (uint32_t i = 0; i < count_props; ++i) {
+    connector->props[i] = mock_connector->properties[i].id;
+    connector->prop_values[i] = mock_connector->properties[i].value;
+  }
+
+  // Copy modes.
+  const uint32_t count_modes = mock_connector->modes.size();
+  connector->count_modes = count_modes;
+  connector->modes = DrmAllocator<drmModeModeInfo>(count_modes);
+  for (uint32_t i = 0; i < count_modes; ++i) {
+    const gfx::Size resoluton = mock_connector->modes[i].first;
+    const uint32_t vrefresh = mock_connector->modes[i].second;
+    connector->modes[i].hdisplay = resoluton.width();
+    connector->modes[i].vdisplay = resoluton.height();
+    connector->modes[i].vrefresh = vrefresh;
+  }
+
+  // Copy associated encoders.
+  const uint32_t count_encoders = mock_connector->encoders.size();
+  connector->count_encoders = count_encoders;
+  connector->encoders = DrmAllocator<uint32_t>(count_encoders);
+  for (uint32_t i = 0; i < count_encoders; ++i)
+    connector->encoders[i] = mock_connector->encoders[i];
+
+  return connector;
+}
+
+ScopedDrmEncoderPtr MockDrmDevice::GetEncoder(uint32_t encoder_id) const {
+  const EncoderProperties* mock_encoder =
+      FindObjectById(encoder_id, drm_state_.encoder_properties);
+  if (!mock_encoder)
+    return nullptr;
+
+  ScopedDrmEncoderPtr encoder(DrmAllocator<drmModeEncoder>());
+  encoder->encoder_id = mock_encoder->id;
+  encoder->possible_crtcs = mock_encoder->possible_crtcs;
+
+  return encoder;
 }
 
 bool MockDrmDevice::AddFramebuffer2(uint32_t width,
@@ -525,6 +636,9 @@
   ScopedDrmPropertyPtr property(DrmAllocator<drmModePropertyRes>());
   property->prop_id = id;
   strcpy(property->name, it->second.c_str());
+  if (IsPropertyValueBlob(property->prop_id))
+    property->flags = DRM_MODE_PROP_BLOB;
+
   return property;
 }
 
@@ -567,7 +681,24 @@
 ScopedDrmPropertyBlobPtr MockDrmDevice::GetPropertyBlob(
     drmModeConnector* connector,
     const char* name) const {
-  return ScopedDrmPropertyBlobPtr(DrmAllocator<drmModePropertyBlobRes>());
+  const ConnectorProperties* mock_connector =
+      FindObjectById(connector->connector_id, drm_state_.connector_properties);
+  if (!mock_connector)
+    return nullptr;
+
+  ScopedDrmPropertyBlobPtr blob(DrmAllocator<drmModePropertyBlobRes>());
+  for (const auto& prop : mock_connector->properties) {
+    auto prop_name_it = drm_state_.property_names.find(prop.id);
+    if (prop_name_it == drm_state_.property_names.end())
+      continue;
+
+    if (prop_name_it->second.compare(name) != 0)
+      continue;
+
+    return GetPropertyBlob(prop.value);
+  }
+
+  return nullptr;
 }
 
 bool MockDrmDevice::SetObjectProperty(uint32_t object_id,
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.h b/ui/ozone/platform/drm/gpu/mock_drm_device.h
index b137432..d891b44 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.h
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.h
@@ -30,6 +30,8 @@
 
 namespace ui {
 
+using ResolutionAndRefreshRate = std::pair<gfx::Size, uint32_t>;
+
 template <class Object>
 Object* DrmAllocator(size_t num_of_objects = 1) {
   return static_cast<Object*>(drmMalloc(num_of_objects * sizeof(Object)));
@@ -40,10 +42,12 @@
 constexpr uint32_t kCrtcIdBase = 200;
 constexpr uint32_t kConnectorIdBase = 300;
 constexpr uint32_t kInFormatsBlobIdBase = 400;
+constexpr uint32_t kEncoderIdBase = 500;
 
 // Required Connector Property IDs:
 constexpr uint32_t kCrtcIdPropId = 1000;
 constexpr uint32_t kLinkStatusPropId = 1001;
+constexpr uint32_t kEdidBlobPropId = 1002;
 
 // Required CRTC Property IDs:
 constexpr uint32_t kActivePropId = 2000;
@@ -78,6 +82,9 @@
 constexpr uint32_t kPlaneCtmId = 5002;
 constexpr uint32_t kRotationPropId = 5003;
 
+// Blob IDs:
+constexpr uint32_t kBaseBlobId = 6000;
+
 // The real DrmDevice makes actual DRM calls which we can't use in unit tests.
 class MockDrmDevice : public DrmDevice {
  public:
@@ -97,10 +104,23 @@
     ~ConnectorProperties();
 
     uint32_t id;
+    bool connection;
+    std::vector<ResolutionAndRefreshRate> modes;
+    std::vector<uint32_t> encoders;
+    std::vector<uint8_t> edid_blob;
 
     std::vector<DrmWrapper::Property> properties;
   };
 
+  struct EncoderProperties {
+    EncoderProperties();
+    EncoderProperties(const EncoderProperties&);
+    ~EncoderProperties();
+
+    uint32_t id;
+    uint32_t possible_crtcs;
+  };
+
   struct PlaneProperties {
     PlaneProperties();
     PlaneProperties(const PlaneProperties&);
@@ -137,6 +157,9 @@
         size_t planes_per_crtc,
         size_t movable_planes = 0u);
 
+    ConnectorProperties& AddConnector();
+    EncoderProperties& AddEncoder();
+    CrtcProperties& AddCrtc();
     std::pair<CrtcProperties&, ConnectorProperties&> AddCrtcAndConnector();
     PlaneProperties& AddPlane(uint32_t crtc_id, uint32_t type);
     PlaneProperties& AddPlane(const std::vector<uint32_t>& crtc_ids,
@@ -144,11 +167,16 @@
 
     std::vector<CrtcProperties> crtc_properties;
     std::vector<ConnectorProperties> connector_properties;
+    std::vector<EncoderProperties> encoder_properties;
     std::vector<PlaneProperties> plane_properties;
+    std::vector<DrmWrapper::Property> blobs;
     std::map<uint32_t, std::string> property_names;
   };
 
   explicit MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device);
+  explicit MockDrmDevice(const base::FilePath& path,
+                         std::unique_ptr<GbmDevice> gbm_device,
+                         bool is_primary_device);
 
   MockDrmDevice(const MockDrmDevice&) = delete;
   MockDrmDevice& operator=(const MockDrmDevice&) = delete;
@@ -205,8 +233,12 @@
     return it != crtc_cursor_map_.end() ? it->second : 0;
   }
 
-  void InitializeState(const MockDrmState& state, bool use_atomic);
-  bool InitializeStateWithResult(const MockDrmState& state, bool use_atomic);
+  void InitializeState(MockDrmState& state, bool use_atomic);
+  bool InitializeStateWithResult(MockDrmState& state, bool use_atomic);
+
+  // Sets EDID blobs as property blobs so they can be fetched when needed via
+  // GetPropertyBlob().
+  void MaybeSetEdidBlobsForConnectors(MockDrmState& state);
 
   void UpdateStateBesidesPlaneManager(const MockDrmState& state);
 
@@ -253,6 +285,7 @@
                const drmModeModeInfo& mode) override;
   bool DisableCrtc(uint32_t crtc_id) override;
   ScopedDrmConnectorPtr GetConnector(uint32_t connector_id) const override;
+  ScopedDrmEncoderPtr GetEncoder(uint32_t encoder_id) const override;
   bool AddFramebuffer2(uint32_t width,
                        uint32_t height,
                        uint32_t format,
@@ -326,12 +359,12 @@
 
   bool ValidatePropertyValue(uint32_t id, uint64_t value);
 
-  int set_crtc_call_count_;
-  int add_framebuffer_call_count_;
-  int remove_framebuffer_call_count_;
-  int page_flip_call_count_;
-  int overlay_clear_call_count_;
-  int allocate_buffer_count_;
+  int set_crtc_call_count_ = 0;
+  int add_framebuffer_call_count_ = 0;
+  int remove_framebuffer_call_count_ = 0;
+  int page_flip_call_count_ = 0;
+  int overlay_clear_call_count_ = 0;
+  int allocate_buffer_count_ = 0;
   int test_modeset_count_ = 0;
   int commit_modeset_count_ = 0;
   int seamless_modeset_count_ = 0;
@@ -341,15 +374,15 @@
   int last_planes_committed_count_ = 0;
   int modeset_sequence_id_ = 0;
 
-  bool set_crtc_expectation_;
-  bool add_framebuffer_expectation_;
-  bool page_flip_expectation_;
-  bool create_dumb_buffer_expectation_;
+  bool set_crtc_expectation_ = true;
+  bool add_framebuffer_expectation_ = true;
+  bool page_flip_expectation_ = true;
+  bool create_dumb_buffer_expectation_ = true;
   bool legacy_gamma_ramp_expectation_ = false;
   bool commit_expectation_ = true;
   bool modeset_with_overlays_expectation_ = true;
 
-  uint32_t current_framebuffer_;
+  uint32_t current_framebuffer_ = 0;
 
   absl::optional<std::string> driver_name_ = "mock";
 
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index 7d16ae2..5525a2a 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -183,7 +183,11 @@
 SkPath Checkbox::GetFocusRingPath() const {
   SkPath path;
   gfx::Rect bounds = image()->GetMirroredContentsBounds();
-  bounds.Inset(1);
+  // Don't add extra insets in the ChromeRefresh case so that the focus ring can
+  // be drawn in the ChromeRefresh style.
+  if (!features::IsChromeRefresh2023()) {
+    bounds.Inset(1);
+  }
   path.addRect(RectToSkRect(bounds));
   return path;
 }
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
index de88e391..8d832bd 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -114,7 +114,8 @@
       }
 
       :host-context([chrome-refresh-2023]) #checkbox:focus-visible {
-        border: 2px solid var(--cr-focus-outline-color);
+        border: none;
+        outline: 2px solid var(--cr-focus-outline-color);
       }
 
       #checkmark {
@@ -140,8 +141,7 @@
 
       :host-context([chrome-refresh-2023]):host([checked]) #checkbox:focus-visible {
         background-clip: padding-box;
-        border-color: transparent;
-        outline: 2px solid var(--cr-focus-outline-color);
+        border: 2px solid transparent;
       }
 
       paper-ripple {
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html
index 5413e78..8d3b34e 100644
--- a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html
+++ b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html
@@ -201,6 +201,13 @@
         --cr-toggle-knob-diameter_: 14px;
       }
 
+      :host-context([chrome-refresh-2023]):host([checked]:active) #knob,
+      :host-context([chrome-refresh-2023]):host([checked]:hover) #knob {
+        --cr-toggle-checked-button-color:
+            var(--color-toggle-button-thumb-on-hover-pressed,
+                var(--cr-fallback-color-primary-container));
+      }
+
       :host-context([chrome-refresh-2023]):host(:hover) #knob::before {
         background-color: var(--cr-hover-background-color);
         border-radius: 50%;
@@ -213,7 +220,6 @@
         width: var(--cr-toggle-ripple-diameter);
       }
 
-
       paper-ripple {
         --paper-ripple-opacity: 1;
         color: var(--cr-toggle-unchecked-ripple-color);
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 3e43348..67d1f5bd 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -413,6 +413,7 @@
     "//components/content_capture/browser",
     "//components/content_capture/common",
     "//components/content_capture/renderer",
+    "//components/content_relationship_verification",
     "//components/content_settings/browser",
     "//components/content_settings/common:mojom",
     "//components/content_settings/common:mojom",
@@ -421,7 +422,6 @@
     "//components/crash/content/browser",
     "//components/crash/core/app",
     "//components/crash/core/common",
-    "//components/digital_asset_links",
     "//components/download/content/factory",
     "//components/download/content/public",
     "//components/download/public/background_service:public",
diff --git a/weblayer/browser/DEPS b/weblayer/browser/DEPS
index a64f693..51f6bf2 100644
--- a/weblayer/browser/DEPS
+++ b/weblayer/browser/DEPS
@@ -21,7 +21,7 @@
   "+components/content_settings/core/common",
   "+components/crash/content/browser",
   "+components/crash/core/common",
-  "+components/digital_asset_links",
+  "+components/content_relationship_verification",
   "+components/download/content/factory",
   "+components/download/content/public",
   "+components/download/public/background_service",
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index e8fefc86..47c2b9b 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -231,11 +231,11 @@
     "//components/browser_ui/widget/android:java",
     "//components/component_updater/android:embedded_component_loader_java",
     "//components/content_capture/android:java",
+    "//components/content_relationship_verification/android:java",
     "//components/content_settings/android:content_settings_enums_java",
     "//components/content_settings/android:java",
     "//components/crash/android:handler_java",
     "//components/crash/android:java",
-    "//components/digital_asset_links/android:java",
     "//components/download/internal/background_service:internal_java",
     "//components/download/internal/common:internal_java",
     "//components/download/network:network_java",
@@ -424,9 +424,9 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
-    "//components/digital_asset_links:java",
-    "//components/digital_asset_links/android:java",
-    "//components/digital_asset_links/android:junit_test_support",
+    "//components/content_relationship_verification:java",
+    "//components/content_relationship_verification/android:java",
+    "//components/content_relationship_verification/android:junit_test_support",
     "//components/embedder_support/android:util_java",
     "//components/payments/content/android:java",
     "//components/payments/content/android:junit_test_support",
diff --git a/weblayer/browser/java/DEPS b/weblayer/browser/java/DEPS
index e4cb03bf..ff5151a4 100644
--- a/weblayer/browser/java/DEPS
+++ b/weblayer/browser/java/DEPS
@@ -6,7 +6,7 @@
   "+components/content_capture/android",
   "+components/content_settings/android/java",
   "+components/crash/android/java",
-  "+components/digital_asset_links/android/java",
+  "+components/content_relationship_verification/android/java",
   "+components/external_intents",
   "+components/infobars/android",
   "+components/location/android/java/src/org/chromium/components/location",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerificationScheduler.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerificationScheduler.java
index 96c7213..55a9dc8 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerificationScheduler.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerificationScheduler.java
@@ -8,9 +8,9 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
-import org.chromium.components.digital_asset_links.OriginVerificationScheduler;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifierHelper;
+import org.chromium.components.content_relationship_verification.OriginVerificationScheduler;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifierHelper;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.content_public.browser.BrowserContextHandle;
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifier.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifier.java
index 8b24093..04709e7 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifier.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifier.java
@@ -12,8 +12,8 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.Relationship;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.Relationship;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.BrowserContextHandle;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifierTest.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifierTest.java
index 0a1f24f..a73b234 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifierTest.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerOriginVerifierTest.java
@@ -25,11 +25,11 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.components.digital_asset_links.OriginVerifier;
-import org.chromium.components.digital_asset_links.OriginVerifier.OriginVerificationListener;
-import org.chromium.components.digital_asset_links.OriginVerifierJni;
-import org.chromium.components.digital_asset_links.OriginVerifierUnitTestSupport;
-import org.chromium.components.digital_asset_links.RelationshipCheckResult;
+import org.chromium.components.content_relationship_verification.OriginVerifier;
+import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
+import org.chromium.components.content_relationship_verification.OriginVerifierJni;
+import org.chromium.components.content_relationship_verification.OriginVerifierUnitTestSupport;
+import org.chromium.components.content_relationship_verification.RelationshipCheckResult;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.content_public.browser.BrowserContextHandle;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerVerificationResultStore.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerVerificationResultStore.java
index fc79933..8dbe48c 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerVerificationResultStore.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerVerificationResultStore.java
@@ -4,7 +4,7 @@
 
 package org.chromium.weblayer_private;
 
-import org.chromium.components.digital_asset_links.VerificationResultStore;
+import org.chromium.components.content_relationship_verification.VerificationResultStore;
 
 import java.util.Collections;
 import java.util.HashSet;
diff --git a/weblayer/browser/navigation_controller_impl.cc b/weblayer/browser/navigation_controller_impl.cc
index 585b96a..3d29e46 100644
--- a/weblayer/browser/navigation_controller_impl.cc
+++ b/weblayer/browser/navigation_controller_impl.cc
@@ -12,7 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "build/build_config.h"
-#include "components/digital_asset_links/response_header_verifier.h"
+#include "components/content_relationship_verification/response_header_verifier.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -563,10 +563,10 @@
       !navigation_handle->IsErrorPage()) {
     if (!navigation_handle->IsSameDocument()) {
       navigation->set_consenting_content(
-          digital_asset_links::ResponseHeaderVerifier::Verify(
+          content_relationship_verification::ResponseHeaderVerifier::Verify(
               tab_->browser()->GetPackageName(),
               navigation->GetNormalizedHeader(
-                  digital_asset_links::kEmbedderAncestorHeader)));
+                  content_relationship_verification::kEmbedderAncestorHeader)));
     }
 #if BUILDFLAG(IS_ANDROID)
     if (java_controller_) {
diff --git a/weblayer/browser/popup_navigation_delegate_impl.cc b/weblayer/browser/popup_navigation_delegate_impl.cc
index 9becc401..6812e86 100644
--- a/weblayer/browser/popup_navigation_delegate_impl.cc
+++ b/weblayer/browser/popup_navigation_delegate_impl.cc
@@ -33,7 +33,7 @@
   return original_user_gesture_;
 }
 
-const GURL& PopupNavigationDelegateImpl::GetURL() {
+GURL PopupNavigationDelegateImpl::GetURL() {
   return params_.url;
 }
 
diff --git a/weblayer/browser/popup_navigation_delegate_impl.h b/weblayer/browser/popup_navigation_delegate_impl.h
index b5651125..f745a03 100644
--- a/weblayer/browser/popup_navigation_delegate_impl.h
+++ b/weblayer/browser/popup_navigation_delegate_impl.h
@@ -22,7 +22,7 @@
   // blocked_content::PopupNavigationDelegate:
   content::RenderFrameHost* GetOpener() override;
   bool GetOriginalUserGesture() override;
-  const GURL& GetURL() override;
+  GURL GetURL() override;
   NavigateResult NavigateWithGesture(
       const blink::mojom::WindowFeatures& window_features,
       absl::optional<WindowOpenDisposition> updated_disposition) override;