diff --git a/DEPS b/DEPS
index 491c2538..a22b7c7 100644
--- a/DEPS
+++ b/DEPS
@@ -304,15 +304,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': '7777ee2bf7ef53e743c2aca75ad988ab74aecd38',
+  'skia_revision': '47b0db43f6a4efa2d66a7c00efd703dbd95d18d7',
   # 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': '2662ff95341e023ac32fbc012ffce2a8b74c5af6',
+  'v8_revision': '9888bdc17e5d64462eaf16fe35a11829ec881250',
   # 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': '0ea8e55a6f0667c2bb189d95987833231cdb22b2',
+  'angle_revision': '249ff1206be30d3b6411bd3524ef50f0776ca66e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '44f86322fc4869ad9c6f9290b9ccaaee9236b6f5',
+  'pdfium_revision': 'bb4e26d0b080637056d169dcbc10b35a71d14808',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -391,7 +391,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'ca51bbf258c6a314b6435849135b0e020ac35041',
+  'devtools_frontend_revision': '131dc50a25d0e3a06b10eb8a89011aff81adf734',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -431,7 +431,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': '6d385e414cc076878d86bc335e5d5296fa98dee2',
+  'dawn_revision': 'c8cb8d03c613ecb7a1948e5cb905ce7817972ebd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -467,7 +467,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': 'dd165f73ae713f58842ad903f5def53de896547d',
+  'cros_components_revision': '592a01c0ce17bb9fd2215f44f18d00f95b4ce4b7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -841,7 +841,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '0562095ce634bd10e5fc77e6ac3f382d8f6d4f05',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f4d6eb970341abeb130a51f0fcf4baa2f1257e80',
       'condition': 'checkout_ios',
   },
 
@@ -1001,7 +1001,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'OHveK-sQfZYVc9OYLG-gzd5LUi8BLlUn70JqB7cZEqIC',
+          'version': 'Z67bBuZpz3Ly3olwQzv_t9xhOsyZ_5cBptIeNZOwCvgC',
       },
     ],
     'condition': 'checkout_android',
@@ -1248,7 +1248,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '95bd968fa3d42996225f25a8dbc9be254ad15251',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'e09f8ea2f5707fa6278386b6bc9f0fff462598cc',
     'condition': 'checkout_src_internal',
   },
 
@@ -1715,7 +1715,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '922cf5c76a1ef4eb6fd120d86af5fe54389913f2',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'df614ae1dd57e6a80ea54961bdce77583b3787bd',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1755,7 +1755,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'KD3NOxZDyxYRBipxZoMEB7jVz3Hqmg2T0ARs8l9WUL4C',
+              'version': '0LMjUUpzAmxHrlftkzbDAaeuTDNXrOEgTgyJm8qIyvMC',
           },
       ],
       'condition': 'checkout_android',
@@ -1900,7 +1900,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '480edec387e8cd5bf5934680050c59a3f7a01438',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '063b45bed7636d2b038d8f6f0913441a00bc5474',
+    Var('webrtc_git') + '/src.git' + '@' + '4ad141e69b8e6a1650450e97b323a159883e5141',
 
   # 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.
@@ -2058,7 +2058,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': '1W0p2evCmBF1pgIbfTezNjWwh9vOVWKz5iezDlQ0qlQC',
+        'version': 'huCVzuCqtTE8iuBuh3lL0zX3FxxVLYwMIFwKsrOTuPwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4194,7 +4194,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'bc3ffc419b6469704f3493c3b572c2ff5f3fb1d5',
+        'b14adbbeda8c478b8a36f34b816cc7d4639edc86',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/ash/webui/diagnostics_ui/backend/system/system_routine_controller.cc b/ash/webui/diagnostics_ui/backend/system/system_routine_controller.cc
index 906cef7..6b48c14 100644
--- a/ash/webui/diagnostics_ui/backend/system/system_routine_controller.cc
+++ b/ash/webui/diagnostics_ui/backend/system/system_routine_controller.cc
@@ -322,6 +322,7 @@
     case mojom::RoutineType::kMemory:
       AcquireWakeLock();
       diagnostics_service_->RunMemoryRoutine(
+          absl::nullopt,
           base::BindOnce(&SystemRoutineController::OnRoutineStarted,
                          weak_factory_.GetWeakPtr(), routine_type));
       memory_routine_start_timestamp_ = base::Time::Now();
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index 84d698ad..ad0fe228 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -199,7 +199,6 @@
   X(TRACE_DISABLED_BY_DEFAULT("blink.debug"))                            \
   X(TRACE_DISABLED_BY_DEFAULT("blink.debug.display_lock"))               \
   X(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout"))                     \
-  X(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"))          \
   X(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees"))               \
   X(TRACE_DISABLED_BY_DEFAULT("blink.feature_usage"))                    \
   X(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"))                   \
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 341a69c..dc8034f 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -16,6 +16,7 @@
 import("//build/config/mac/mac_sdk_overrides.gni")
 
 import("//build/config/pch.gni")
+import("//build/config/rust.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build/config/ui.gni")
 import("//build/toolchain/goma.gni")
@@ -272,25 +273,31 @@
 
 # Only the executable template in BUILDCONFIG.gn should reference this.
 group("executable_deps") {
-  public_deps = [ ":common_deps" ]
-  if (export_libcxxabi_from_executables) {
-    public_deps += [ "//buildtools/third_party/libc++abi" ]
+  if (!toolchain_for_rust_host_build_tools) {
+    public_deps = [ ":common_deps" ]
+    if (export_libcxxabi_from_executables) {
+      public_deps += [ "//buildtools/third_party/libc++abi" ]
+    }
+    public_configs = [ "//build/config/sanitizers:link_executable" ]
   }
-  public_configs = [ "//build/config/sanitizers:link_executable" ]
 }
 
 # Only the loadable_module template in BUILDCONFIG.gn should reference this.
 group("loadable_module_deps") {
-  public_deps = [ ":common_deps" ]
+  if (!toolchain_for_rust_host_build_tools) {
+    public_deps = [ ":common_deps" ]
 
-  public_configs = [ "//build/config/sanitizers:link_shared_library" ]
+    public_configs = [ "//build/config/sanitizers:link_shared_library" ]
+  }
 }
 
 # Only the shared_library template in BUILDCONFIG.gn should reference this.
 group("shared_library_deps") {
-  public_deps = [ ":common_deps" ]
+  if (!toolchain_for_rust_host_build_tools) {
+    public_deps = [ ":common_deps" ]
 
-  public_configs = [ "//build/config/sanitizers:link_shared_library" ]
+    public_configs = [ "//build/config/sanitizers:link_shared_library" ]
+  }
 }
 
 # Executable configs -----------------------------------------------------------
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 6a8778e..f91d0e3a 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6664,6 +6664,12 @@
   <message name="IDS_OOBE_DISPLAY_SIZE_VALUE" desc="The currently selected display size percentage.">
     <ph name="DISPLAY_ZOOM">$1<ex>120</ex></ph>%
   </message>
+  <message name="IDS_OOBE_DISPLAY_SIZE_POSITIVE_BUTTON_ARIA" desc="The message to be announced by ChromeVox in the display size screen when the button to increase the size is focused" translateable="false">
+    Increase display and text size
+  </message>
+  <message name="IDS_OOBE_DISPLAY_SIZE_NEGATIVE_BUTTON_ARIA" desc="The message to be announced by ChromeVox in the display size screen when the button to decrease the size is focused" translateable="false">
+    Decrease display and text size
+  </message>
 
   <!-- Drive Offline-->
   <!-- TODO(b/278024582): Remove translateable tags when strings are finalized. -->
diff --git a/chrome/browser/3pcd_heuristics/BUILD.gn b/chrome/browser/3pcd_heuristics/BUILD.gn
index 0e584d8..3517461 100644
--- a/chrome/browser/3pcd_heuristics/BUILD.gn
+++ b/chrome/browser/3pcd_heuristics/BUILD.gn
@@ -4,7 +4,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "opener_heuristic_metrics_unittest.cc" ]
+  sources = [
+    "opener_heuristic_metrics_unittest.cc",
+    "opener_heuristic_utils_unittest.cc",
+  ]
 
   deps = [
     "//base",
diff --git a/chrome/browser/3pcd_heuristics/opener_heuristic_browsertest.cc b/chrome/browser/3pcd_heuristics/opener_heuristic_browsertest.cc
index 8deb2611..535d378f 100644
--- a/chrome/browser/3pcd_heuristics/opener_heuristic_browsertest.cc
+++ b/chrome/browser/3pcd_heuristics/opener_heuristic_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/3pcd_heuristics/opener_heuristic_metrics.h"
 #include "chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.h"
+#include "chrome/browser/3pcd_heuristics/opener_heuristic_utils.h"
 #include "chrome/browser/dips/dips_service.h"
 #include "chrome/browser/dips/dips_storage.h"
 #include "chrome/browser/dips/dips_test_utils.h"
@@ -105,6 +106,7 @@
     host_resolver()->AddRule("b.test", "127.0.0.1");
     host_resolver()->AddRule("sub.b.test", "127.0.0.1");
     host_resolver()->AddRule("c.test", "127.0.0.1");
+    host_resolver()->AddRule("google.com", "127.0.0.1");
     DIPSService::Get(GetActiveWebContents()->GetBrowserContext())
         ->SetStorageClockForTesting(&clock_);
   }
@@ -165,6 +167,20 @@
     destruction_watcher.Wait();
   }
 
+  base::expected<OptionalBool, std::string> GetOpenerHasSameSiteIframe(
+      ukm::TestUkmRecorder& ukm_recorder,
+      const std::string& entry_name) {
+    auto entries =
+        ukm_recorder.GetEntries(entry_name, {"OpenerHasSameSiteIframe"});
+    if (entries.size() != 1) {
+      return base::unexpected(
+          base::StringPrintf("Expected 1 %s entry, found %zu",
+                             entry_name.c_str(), entries.size()));
+    }
+    return static_cast<OptionalBool>(
+        entries[0].metrics["OpenerHasSameSiteIframe"]);
+  }
+
   base::SimpleTestClock clock_;
 };
 
@@ -442,6 +458,11 @@
             toplevel_url);
   EXPECT_EQ(entries[0].metrics["HasSameSiteIframe"],
             static_cast<int32_t>(OptionalBool::kFalse));
+
+  auto opener_has_iframe = GetOpenerHasSameSiteIframe(
+      ukm_recorder, "OpenerHeuristic.PopupPastInteraction");
+  ASSERT_TRUE(opener_has_iframe.has_value()) << opener_has_iframe.error();
+  EXPECT_EQ(opener_has_iframe.value(), OptionalBool::kFalse);
 }
 
 IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest,
@@ -470,6 +491,11 @@
             toplevel_url);
   EXPECT_EQ(entries[0].metrics["HasSameSiteIframe"],
             static_cast<int32_t>(OptionalBool::kFalse));
+
+  auto opener_has_iframe = GetOpenerHasSameSiteIframe(
+      ukm_recorder, "OpenerHeuristic.PopupInteraction");
+  ASSERT_TRUE(opener_has_iframe.has_value()) << opener_has_iframe.error();
+  EXPECT_EQ(opener_has_iframe.value(), OptionalBool::kFalse);
 }
 
 IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest,
@@ -499,6 +525,11 @@
             toplevel_url);
   EXPECT_EQ(entries[0].metrics["HasSameSiteIframe"],
             static_cast<int32_t>(OptionalBool::kTrue));
+
+  auto opener_has_iframe = GetOpenerHasSameSiteIframe(
+      ukm_recorder, "OpenerHeuristic.PopupPastInteraction");
+  ASSERT_TRUE(opener_has_iframe.has_value()) << opener_has_iframe.error();
+  EXPECT_EQ(opener_has_iframe.value(), OptionalBool::kTrue);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -530,6 +561,11 @@
             toplevel_url);
   EXPECT_EQ(entries[0].metrics["HasSameSiteIframe"],
             static_cast<int32_t>(OptionalBool::kUnknown));
+
+  auto opener_has_iframe = GetOpenerHasSameSiteIframe(
+      ukm_recorder, "OpenerHeuristic.PopupInteraction");
+  ASSERT_TRUE(opener_has_iframe.has_value()) << opener_has_iframe.error();
+  EXPECT_EQ(opener_has_iframe.value(), OptionalBool::kUnknown);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -563,4 +599,75 @@
             toplevel_url);
   EXPECT_EQ(entries[0].metrics["HasSameSiteIframe"],
             static_cast<int32_t>(OptionalBool::kUnknown));
+
+  auto opener_has_iframe = GetOpenerHasSameSiteIframe(
+      ukm_recorder, "OpenerHeuristic.PopupInteraction");
+  ASSERT_TRUE(opener_has_iframe.has_value()) << opener_has_iframe.error();
+  EXPECT_EQ(opener_has_iframe.value(), OptionalBool::kUnknown);
+}
+
+IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest, TopLevel_PopupProvider) {
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  GURL toplevel_url = embedded_test_server()->GetURL("a.test", "/title1.html");
+  GURL popup_url = embedded_test_server()->GetURL("google.com", "/title1.html");
+  WebContents* web_contents = GetActiveWebContents();
+
+  RecordInteraction(GURL("https://google.com"), clock_.Now() - base::Hours(3));
+
+  ASSERT_TRUE(content::NavigateToURL(web_contents, toplevel_url));
+  ASSERT_TRUE(OpenPopup(popup_url).has_value());
+
+  auto entries =
+      ukm_recorder.GetEntries("OpenerHeuristic.TopLevel", {"PopupProvider"});
+  ASSERT_EQ(entries.size(), 1u);
+  EXPECT_EQ(ukm_recorder.GetSourceForSourceId(entries[0].source_id)->url(),
+            toplevel_url);
+  EXPECT_EQ(entries[0].metrics["PopupProvider"],
+            static_cast<int64_t>(PopupProvider::kGoogle));
+}
+
+IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest, TopLevel_PopupId) {
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  GURL toplevel_url = embedded_test_server()->GetURL("a.test", "/title1.html");
+  GURL popup_url = embedded_test_server()->GetURL("google.com", "/title1.html");
+  WebContents* web_contents = GetActiveWebContents();
+
+  RecordInteraction(GURL("https://google.com"), clock_.Now() - base::Hours(3));
+
+  ASSERT_TRUE(content::NavigateToURL(web_contents, toplevel_url));
+  auto maybe_popup = OpenPopup(popup_url);
+  ASSERT_TRUE(maybe_popup.has_value()) << maybe_popup.error();
+
+  SimulateMouseClick(*maybe_popup);
+
+  // Verify all three events share the same popup id.
+  auto tl_entries =
+      ukm_recorder.GetEntries("OpenerHeuristic.TopLevel", {"PopupId"});
+  ASSERT_EQ(tl_entries.size(), 1u);
+  EXPECT_EQ(ukm_recorder.GetSourceForSourceId(tl_entries[0].source_id)->url(),
+            toplevel_url);
+  const int64_t popup_id = tl_entries[0].metrics["PopupId"];
+  EXPECT_NE(popup_id, 0);
+
+  auto pi_entries =
+      ukm_recorder.GetEntries("OpenerHeuristic.PopupInteraction", {"PopupId"});
+  ASSERT_EQ(pi_entries.size(), 1u);
+  EXPECT_EQ(ukm_recorder.GetSourceForSourceId(pi_entries[0].source_id)->url(),
+            popup_url);
+  EXPECT_EQ(pi_entries[0].metrics["PopupId"], popup_id);
+
+  auto ppi_entries = ukm_recorder.GetEntries(
+      "OpenerHeuristic.PopupPastInteraction", {"PopupId"});
+  ASSERT_EQ(ppi_entries.size(), 1u);
+  EXPECT_EQ(ukm_recorder.GetSourceForSourceId(ppi_entries[0].source_id)->url(),
+            popup_url);
+  EXPECT_EQ(ppi_entries[0].metrics["PopupId"], popup_id);
+
+  // Open second popup, verify different popup id.
+  ASSERT_TRUE(OpenPopup(popup_url).has_value());
+  tl_entries = ukm_recorder.GetEntries("OpenerHeuristic.TopLevel", {"PopupId"});
+  ASSERT_EQ(tl_entries.size(), 2u);
+  const int64_t popup_id2 = tl_entries[1].metrics["PopupId"];
+  EXPECT_NE(popup_id2, 0);
+  EXPECT_NE(popup_id, popup_id2);
 }
diff --git a/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.cc b/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.cc
index 20d71a6f..e235b2dd 100644
--- a/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.cc
+++ b/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.cc
@@ -7,10 +7,12 @@
 #include <utility>
 
 #include "base/memory/weak_ptr.h"
+#include "base/rand_util.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/3pcd_heuristics/opener_heuristic_metrics.h"
+#include "chrome/browser/3pcd_heuristics/opener_heuristic_utils.h"
 #include "chrome/browser/dips/dips_bounce_detector.h"
 #include "chrome/browser/dips/dips_service.h"
 #include "chrome/browser/dips/dips_utils.h"
@@ -153,6 +155,7 @@
     const GURL& initial_url,
     base::WeakPtr<OpenerHeuristicTabHelper> opener)
     : content::WebContentsObserver(web_contents),
+      popup_id_(static_cast<int32_t>(base::RandUint64())),
       initial_url_(initial_url),
       opener_(opener),
       opener_page_id_(opener->page_id()),
@@ -182,13 +185,16 @@
     return;
   }
 
+  auto has_iframe = GetOpenerHasSameSiteIframe(initial_url_);
   ukm::builders::OpenerHeuristic_PopupPastInteraction(
       initial_source_id_.value())
       .SetHoursSinceLastInteraction(
           BucketizeHoursSinceLastInteraction(time_since_interaction_.value()))
+      .SetOpenerHasSameSiteIframe(static_cast<int64_t>(has_iframe))
+      .SetPopupId(popup_id_)
       .Record(ukm::UkmRecorder::Get());
 
-  EmitTopLevel(initial_url_);
+  EmitTopLevel(has_iframe);
 }
 
 void OpenerHeuristicTabHelper::PopupObserver::DidFinishNavigation(
@@ -238,26 +244,43 @@
   }
 
   auto time_since_committed = GetClock()->Now() - *commit_time_;
+  auto has_iframe =
+      GetOpenerHasSameSiteIframe(render_frame_host->GetLastCommittedURL());
   ukm::builders::OpenerHeuristic_PopupInteraction(
       render_frame_host->GetPageUkmSourceId())
       .SetSecondsSinceCommitted(
           BucketizeSecondsSinceCommitted(time_since_committed))
       .SetUrlIndex(url_index_)
+      .SetOpenerHasSameSiteIframe(static_cast<int64_t>(has_iframe))
+      .SetPopupId(popup_id_)
       .Record(ukm::UkmRecorder::Get());
 
   interaction_reported_ = true;
 
-  EmitTopLevel(render_frame_host->GetLastCommittedURL());
+  EmitTopLevel(has_iframe);
 }
 
 void OpenerHeuristicTabHelper::PopupObserver::EmitTopLevel(
-    const GURL& popup_url) {
-  OptionalBool has_iframe = OptionalBool::kUnknown;
-  if (opener_ && opener_->page_id() == opener_page_id_) {
-    has_iframe = ToOptionalBool(opener_->HasSameSiteIframe(popup_url));
+    OptionalBool has_iframe) {
+  if (toplevel_reported_) {
+    return;
   }
 
   ukm::builders::OpenerHeuristic_TopLevel(opener_source_id_)
-      .SetHasSameSiteIframe(static_cast<int32_t>(has_iframe))
+      .SetHasSameSiteIframe(static_cast<int64_t>(has_iframe))
+      .SetPopupProvider(static_cast<int64_t>(GetPopupProvider(initial_url_)))
+      .SetPopupId(popup_id_)
       .Record(ukm::UkmRecorder::Get());
+
+  toplevel_reported_ = true;
+}
+
+OptionalBool
+OpenerHeuristicTabHelper::PopupObserver::GetOpenerHasSameSiteIframe(
+    const GURL& popup_url) {
+  if (opener_ && opener_->page_id() == opener_page_id_) {
+    return ToOptionalBool(opener_->HasSameSiteIframe(popup_url));
+  }
+
+  return OptionalBool::kUnknown;
 }
diff --git a/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.h b/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.h
index 04bf1c6..25c1c76b 100644
--- a/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.h
+++ b/chrome/browser/3pcd_heuristics/opener_heuristic_tab_helper.h
@@ -52,7 +52,9 @@
     // the necessary information.
     void EmitPastInteractionIfReady();
     // Emit the OpenerHeuristic.TopLevel UKM event.
-    void EmitTopLevel(const GURL& popup_url);
+    void EmitTopLevel(OptionalBool has_iframe);
+    // See if the opener page has an iframe from the same site.
+    OptionalBool GetOpenerHasSameSiteIframe(const GURL& popup_url);
 
     // WebContentsObserver overrides:
     void DidFinishNavigation(
@@ -60,6 +62,7 @@
     void FrameReceivedUserActivation(
         content::RenderFrameHost* render_frame_host) override;
 
+    const int32_t popup_id_;
     // The URL originally passed to window.open().
     const GURL initial_url_;
     // The top-level WebContents that opened this pop-up.
@@ -75,6 +78,7 @@
     absl::optional<base::Time> commit_time_;
     size_t url_index_ = 0;
     bool interaction_reported_ = false;
+    bool toplevel_reported_ = false;
   };
 
   ~OpenerHeuristicTabHelper() override;
diff --git a/chrome/browser/3pcd_heuristics/opener_heuristic_utils.cc b/chrome/browser/3pcd_heuristics/opener_heuristic_utils.cc
new file mode 100644
index 0000000..b32e334
--- /dev/null
+++ b/chrome/browser/3pcd_heuristics/opener_heuristic_utils.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 "chrome/browser/3pcd_heuristics/opener_heuristic_utils.h"
+
+#include "url/gurl.h"
+
+PopupProvider GetPopupProvider(const GURL& popup_url) {
+  if (popup_url.DomainIs("google.com")) {
+    return PopupProvider::kGoogle;
+  }
+  return PopupProvider::kUnknown;
+}
diff --git a/chrome/browser/3pcd_heuristics/opener_heuristic_utils.h b/chrome/browser/3pcd_heuristics/opener_heuristic_utils.h
new file mode 100644
index 0000000..2b050671
--- /dev/null
+++ b/chrome/browser/3pcd_heuristics/opener_heuristic_utils.h
@@ -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.
+
+#ifndef CHROME_BROWSER_3PCD_HEURISTICS_OPENER_HEURISTIC_UTILS_H_
+#define CHROME_BROWSER_3PCD_HEURISTICS_OPENER_HEURISTIC_UTILS_H_
+
+class GURL;
+
+enum class PopupProvider {
+  kUnknown = 0,
+  kGoogle = 1,
+};
+
+PopupProvider GetPopupProvider(const GURL& popup_url);
+
+#endif  // CHROME_BROWSER_3PCD_HEURISTICS_OPENER_HEURISTIC_UTILS_H_
diff --git a/chrome/browser/3pcd_heuristics/opener_heuristic_utils_unittest.cc b/chrome/browser/3pcd_heuristics/opener_heuristic_utils_unittest.cc
new file mode 100644
index 0000000..045d229
--- /dev/null
+++ b/chrome/browser/3pcd_heuristics/opener_heuristic_utils_unittest.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 "chrome/browser/3pcd_heuristics/opener_heuristic_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+TEST(OpenerHeuristicUtilsTest, GetPopupProvider) {
+  // Any google.com subdomain.
+  EXPECT_EQ(GetPopupProvider(GURL("https://accounts.google.com/")),
+            PopupProvider::kGoogle);
+  EXPECT_EQ(GetPopupProvider(GURL("https://www.google.com/")),
+            PopupProvider::kGoogle);
+  // Also match http (just in case).
+  EXPECT_EQ(GetPopupProvider(GURL("http://www.google.com/")),
+            PopupProvider::kGoogle);
+
+  // If not a known provider, return kUnknown.
+  EXPECT_EQ(GetPopupProvider(GURL("https://www.example.com/")),
+            PopupProvider::kUnknown);
+}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ca02eec..d0e758a 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -142,6 +142,8 @@
     "3pcd_heuristics/opener_heuristic_metrics.h",
     "3pcd_heuristics/opener_heuristic_tab_helper.cc",
     "3pcd_heuristics/opener_heuristic_tab_helper.h",
+    "3pcd_heuristics/opener_heuristic_utils.cc",
+    "3pcd_heuristics/opener_heuristic_utils.h",
     "about_flags.cc",
     "about_flags.h",
     "accessibility/accessibility_labels_service.cc",
diff --git a/chrome/browser/apps/app_service/BUILD.gn b/chrome/browser/apps/app_service/BUILD.gn
index 5123a1b..c15f8a60 100644
--- a/chrome/browser/apps/app_service/BUILD.gn
+++ b/chrome/browser/apps/app_service/BUILD.gn
@@ -237,6 +237,7 @@
       "//chrome/browser/metrics/structured:features",
       "//chrome/browser/resources",
       "//chromeos/ash/components/login/login_state",
+      "//chromeos/components/kiosk:kiosk",
       "//components/app_restore",
       "//components/arc",
       "//components/arc/common",
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
index 66bd640..b0af8b0 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
+#include "chromeos/components/kiosk/kiosk_utils.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/instance_registry.h"
@@ -374,6 +375,12 @@
   if (ash::DemoSession::IsDeviceInDemoMode()) {
     return true;
   }
+
+  // Bypass AppKM App Sync check for Kiosk devices to collect app metrics.
+  if (chromeos::IsKioskSession()) {
+    return true;
+  }
+
   switch (syncer::GetUploadToGoogleState(
       SyncServiceFactory::GetForProfile(profile), syncer::ModelType::APPS)) {
     case syncer::UploadState::NOT_ACTIVE:
diff --git a/chrome/browser/ash/crosapi/local_printer_ash.cc b/chrome/browser/ash/crosapi/local_printer_ash.cc
index f2a2abc..3ec78c1 100644
--- a/chrome/browser/ash/crosapi/local_printer_ash.cc
+++ b/chrome/browser/ash/crosapi/local_printer_ash.cc
@@ -84,7 +84,6 @@
 // running.
 mojom::CapabilitiesResponsePtr OnSetUpPrinter(
     std::unique_ptr<ash::PrinterConfigurer>,
-    PrefService* prefs,
     const chromeos::Printer& printer,
     const absl::optional<printing::PrinterSemanticCapsAndDefaults>& caps) {
   return mojom::CapabilitiesResponse::New(
@@ -106,7 +105,6 @@
 void OnPrinterAuthenticated(
     std::unique_ptr<ash::printing::PrinterAuthenticator> /* authenticator */,
     std::unique_ptr<ash::PrinterConfigurer> printer_configurer,
-    Profile* profile,
     ash::CupsPrintersManager* printers_manager,
     const chromeos::Printer& printer,
     mojom::LocalPrinter::GetCapabilityCallback callback,
@@ -120,8 +118,7 @@
   ash::PrinterConfigurer* ptr = printer_configurer.get();
   ash::printing::SetUpPrinter(
       printers_manager, ptr, printer,
-      base::BindOnce(OnSetUpPrinter, std::move(printer_configurer),
-                     profile->GetPrefs(), printer)
+      base::BindOnce(OnSetUpPrinter, std::move(printer_configurer), printer)
           .Then(std::move(callback)));
 }
 
@@ -169,18 +166,14 @@
 
 }  // namespace
 
-LocalPrinterAsh::LocalPrinterAsh()
-    : profile_manager_(g_browser_process->profile_manager()) {
-  if (profile_manager_) {
-    profile_manager_->AddObserver(this);
+LocalPrinterAsh::LocalPrinterAsh() {
+  auto* profile_manager = g_browser_process->profile_manager();
+  if (profile_manager) {
+    profile_manager_observer_.Observe(profile_manager);
   }
 }
 
-LocalPrinterAsh::~LocalPrinterAsh() {
-  if (profile_manager_) {
-    profile_manager_->RemoveObserver(this);
-  }
-}
+LocalPrinterAsh::~LocalPrinterAsh() = default;
 
 // static
 mojom::PrintServersConfigPtr LocalPrinterAsh::ConfigToMojom(
@@ -256,8 +249,7 @@
 }
 
 void LocalPrinterAsh::OnProfileManagerDestroying() {
-  profile_manager_->RemoveObserver(this);
-  profile_manager_ = nullptr;
+  profile_manager_observer_.Reset();
 }
 
 void LocalPrinterAsh::OnPrintJobCreated(base::WeakPtr<ash::CupsPrintJob> job) {
@@ -359,8 +351,6 @@
     std::move(callback).Run(nullptr);
     return;
   }
-  std::unique_ptr<ash::PrinterConfigurer> printer_configurer =
-      CreatePrinterConfigurer(profile);
 
   if (ash::features::IsOAuthIppEnabled()) {
     ash::printing::oauth2::AuthorizationZonesManager* auth_manager =
@@ -373,10 +363,10 @@
         authenticator.get();
     authenticator_ptr->ObtainAccessTokenIfNeeded(
         base::BindOnce(OnPrinterAuthenticated, std::move(authenticator),
-                       std::move(printer_configurer), profile, printers_manager,
+                       CreatePrinterConfigurer(profile), printers_manager,
                        *printer, std::move(callback)));
   } else {
-    OnPrinterAuthenticated(nullptr, std::move(printer_configurer), profile,
+    OnPrinterAuthenticated(nullptr, CreatePrinterConfigurer(profile),
                            printers_manager, *printer, std::move(callback),
                            ash::printing::oauth2::StatusCode::kOK, "");
   }
diff --git a/chrome/browser/ash/crosapi/local_printer_ash.h b/chrome/browser/ash/crosapi/local_printer_ash.h
index 008c615..e337fb1 100644
--- a/chrome/browser/ash/crosapi/local_printer_ash.h
+++ b/chrome/browser/ash/crosapi/local_printer_ash.h
@@ -8,9 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ash/printing/cups_print_job.h"
 #include "chrome/browser/ash/printing/cups_print_job_manager.h"
 #include "chrome/browser/ash/printing/print_servers_manager.h"
@@ -138,7 +138,8 @@
       Profile* profile);
   virtual ash::printing::IppClientInfoCalculator* GetIppClientInfoCalculator();
 
-  raw_ptr<ProfileManager, ExperimentalAsh> profile_manager_ = nullptr;
+  base::ScopedObservation<ProfileManager, LocalPrinterAsh>
+      profile_manager_observer_{this};
 
   bool observers_registered_ = false;
 
diff --git a/chrome/browser/ash/login/screens/display_size_screen.cc b/chrome/browser/ash/login/screens/display_size_screen.cc
index e47eb37..8229635 100644
--- a/chrome/browser/ash/login/screens/display_size_screen.cc
+++ b/chrome/browser/ash/login/screens/display_size_screen.cc
@@ -37,7 +37,12 @@
   return factors;
 }
 
-float GetCurrentZoomFactor() {
+float GetCurrentZoomFactor(PrefService* prefs) {
+  if (!prefs->FindPreference(prefs::kOobeDisplaySizeFactorDeferred)
+           ->IsDefaultValue()) {
+    return prefs->GetDouble(prefs::kOobeDisplaySizeFactorDeferred);
+  }
+
   const auto display_id =
       display::Screen::GetScreen()->GetPrimaryDisplay().id();
   const auto& info =
@@ -45,6 +50,11 @@
   return info.zoom_factor();
 }
 
+std::string RetrieveChoobeSubtitle(PrefService* prefs) {
+  int percentage = std::round(GetCurrentZoomFactor(prefs) * 100);
+  return base::NumberToString(percentage) + "%";
+}
+
 bool ShouldShowChoobeReturnButton(ChoobeFlowController* controller) {
   if (!features::IsOobeChoobeEnabled() || !controller) {
     return false;
@@ -182,7 +192,9 @@
 
   base::Value::Dict data;
   data.Set("availableSizes", std::move(factors_list));
-  data.Set("currentSize", GetCurrentZoomFactor());
+  data.Set(
+      "currentSize",
+      GetCurrentZoomFactor(ProfileManager::GetActiveUserProfile()->GetPrefs()));
   data.Set(
       "shouldShowReturn",
       ShouldShowChoobeReturnButton(
@@ -220,15 +232,6 @@
   BaseScreen::OnUserAction(args);
 }
 
-std::string DisplaySizeScreen::RetrieveChoobeSubtitle() {
-  int percentage =
-      std::round(ProfileManager::GetActiveUserProfile()->GetPrefs()->GetDouble(
-                     prefs::kOobeDisplaySizeFactorDeferred) *
-                 100);
-
-  return base::NumberToString(percentage) + "%";
-}
-
 ScreenSummary DisplaySizeScreen::GetScreenSummary() {
   ScreenSummary summary;
   summary.screen_id = DisplaySizeScreenView::kScreenId;
@@ -240,7 +243,8 @@
   if (WizardController::default_controller()
           ->choobe_flow_controller()
           ->IsScreenCompleted(DisplaySizeScreenView::kScreenId)) {
-    summary.subtitle_resource = RetrieveChoobeSubtitle();
+    summary.subtitle_resource = RetrieveChoobeSubtitle(
+        ProfileManager::GetActiveUserProfile()->GetPrefs());
   }
 
   return summary;
diff --git a/chrome/browser/ash/login/screens/display_size_screen.h b/chrome/browser/ash/login/screens/display_size_screen.h
index 3eae332..300a5986 100644
--- a/chrome/browser/ash/login/screens/display_size_screen.h
+++ b/chrome/browser/ash/login/screens/display_size_screen.h
@@ -47,7 +47,6 @@
   void HideImpl() override;
   void OnUserAction(const base::Value::List& args) override;
   ScreenSummary GetScreenSummary() override;
-  std::string RetrieveChoobeSubtitle();
 
   base::WeakPtr<DisplaySizeScreenView> view_;
   ScreenExitCallback exit_callback_;
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index f8b02a2c..30e60b40 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -608,17 +608,6 @@
   return can_lock;
 }
 
-bool ChromeUserManagerImpl::IsUserNonCryptohomeDataEphemeral(
-    const AccountId& account_id) const {
-  // Data belonging to the obsolete device local accounts whose data has not
-  // been removed yet is not ephemeral.
-  const bool is_obsolete_device_local_account =
-      IsDeviceLocalAccountMarkedForRemoval(account_id);
-
-  return !is_obsolete_device_local_account &&
-         ChromeUserManager::IsUserNonCryptohomeDataEphemeral(account_id);
-}
-
 bool ChromeUserManagerImpl::IsEphemeralAccountId(
     const AccountId& account_id) const {
   // Data belonging to the device owner is never ephemeral.
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.h b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
index 037ff14..f038287 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.h
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
@@ -83,8 +83,6 @@
   void SaveUserDisplayName(const AccountId& account_id,
                            const std::u16string& display_name) override;
   bool CanCurrentUserLock() const override;
-  bool IsUserNonCryptohomeDataEphemeral(
-      const AccountId& account_id) const override;
   bool IsGuestSessionAllowed() const override;
   bool IsGaiaUserAllowed(const user_manager::User& user) const override;
   bool IsUserAllowed(const user_manager::User& user) const override;
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc
index bdd4f5b..4ed24e6 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc
@@ -471,9 +471,11 @@
       break;
     }
     case ash::cros_healthd::mojom::DiagnosticRoutineEnum::kMemory: {
-      diagnostics_service->RunMemoryRoutine(base::BindOnce(
-          &DeviceCommandRunRoutineJob::OnCrosHealthdResponseReceived,
-          weak_ptr_factory_.GetWeakPtr(), std::move(result_callback)));
+      diagnostics_service->RunMemoryRoutine(
+          absl::nullopt,
+          base::BindOnce(
+              &DeviceCommandRunRoutineJob::OnCrosHealthdResponseReceived,
+              weak_ptr_factory_.GetWeakPtr(), std::move(result_callback)));
       break;
     }
     case ash::cros_healthd::mojom::DiagnosticRoutineEnum::kLanConnectivity: {
diff --git a/chrome/browser/ash/telemetry_extension/diagnostics/diagnostics_service_ash.cc b/chrome/browser/ash/telemetry_extension/diagnostics/diagnostics_service_ash.cc
index f9f83d33..bc61a6f 100644
--- a/chrome/browser/ash/telemetry_extension/diagnostics/diagnostics_service_ash.cc
+++ b/chrome/browser/ash/telemetry_extension/diagnostics/diagnostics_service_ash.cc
@@ -306,13 +306,16 @@
 
 void DiagnosticsServiceAsh::RunMemoryRoutine(
     RunMemoryRoutineCallback callback) {
-  GetService()->RunMemoryRoutine(base::BindOnce(
-      [](crosapi::mojom::DiagnosticsService::RunMemoryRoutineCallback callback,
-         cros_healthd::mojom::RunRoutineResponsePtr ptr) {
-        std::move(callback).Run(
-            converters::ConvertDiagnosticsPtr(std::move(ptr)));
-      },
-      std::move(callback)));
+  GetService()->RunMemoryRoutine(
+      absl::nullopt,
+      base::BindOnce(
+          [](crosapi::mojom::DiagnosticsService::RunMemoryRoutineCallback
+                 callback,
+             cros_healthd::mojom::RunRoutineResponsePtr ptr) {
+            std::move(callback).Run(
+                converters::ConvertDiagnosticsPtr(std::move(ptr)));
+          },
+          std::move(callback)));
 }
 
 void DiagnosticsServiceAsh::RunNvmeSelfTestRoutine(
diff --git a/chrome/browser/background/background_contents_service_factory.cc b/chrome/browser/background/background_contents_service_factory.cc
index bf4e78d..460e215 100644
--- a/chrome/browser/background/background_contents_service_factory.cc
+++ b/chrome/browser/background/background_contents_service_factory.cc
@@ -25,7 +25,8 @@
 // static
 BackgroundContentsServiceFactory*
 BackgroundContentsServiceFactory::GetInstance() {
-  return base::Singleton<BackgroundContentsServiceFactory>::get();
+  static base::NoDestructor<BackgroundContentsServiceFactory> instance;
+  return instance.get();
 }
 
 BackgroundContentsServiceFactory::BackgroundContentsServiceFactory()
@@ -43,7 +44,7 @@
   DependsOn(NotificationDisplayServiceFactory::GetInstance());
 }
 
-BackgroundContentsServiceFactory::~BackgroundContentsServiceFactory() {}
+BackgroundContentsServiceFactory::~BackgroundContentsServiceFactory() = default;
 
 KeyedService* BackgroundContentsServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
diff --git a/chrome/browser/background/background_contents_service_factory.h b/chrome/browser/background/background_contents_service_factory.h
index cf0c6884..78e3b45 100644
--- a/chrome/browser/background/background_contents_service_factory.h
+++ b/chrome/browser/background/background_contents_service_factory.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_BACKGROUND_BACKGROUND_CONTENTS_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_BACKGROUND_BACKGROUND_CONTENTS_SERVICE_FACTORY_H_
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
 class BackgroundContentsService;
@@ -26,7 +26,7 @@
       const BackgroundContentsServiceFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<BackgroundContentsServiceFactory>;
+  friend base::NoDestructor<BackgroundContentsServiceFactory>;
 
   BackgroundContentsServiceFactory();
   ~BackgroundContentsServiceFactory() override;
diff --git a/chrome/browser/chromeos/kcer_nss/kcer_nss_unittest.cc b/chrome/browser/chromeos/kcer_nss/kcer_nss_unittest.cc
index cc600e6..37d5530 100644
--- a/chrome/browser/chromeos/kcer_nss/kcer_nss_unittest.cc
+++ b/chrome/browser/chromeos/kcer_nss/kcer_nss_unittest.cc
@@ -195,11 +195,15 @@
 // thread.
 class TokenHolder {
  public:
-  explicit TokenHolder(Token token) {
+  explicit TokenHolder(Token token, bool initialize) {
     io_token_ = std::make_unique<internal::KcerTokenImplNss>(token);
     io_token_->SetAttributeTranslationForTesting(/*is_enabled=*/true);
     weak_ptr_ = io_token_->GetWeakPtr();
     // After this point `io_token_` should only be used on the IO thread.
+
+    if (initialize) {
+      Initialize();
+    }
   }
 
   ~TokenHolder() {
@@ -208,6 +212,9 @@
   }
 
   void Initialize() {
+    CHECK(!is_initialized_);
+    is_initialized_ = true;
+
     base::RunLoop run_loop;
 
     IOTaskRunner()->PostTaskAndReply(
@@ -234,6 +241,7 @@
   base::WeakPtr<internal::KcerTokenImplNss> weak_ptr_;
   std::unique_ptr<internal::KcerTokenImplNss> io_token_;
   crypto::ScopedTestNSSDB nss_slot_;
+  bool is_initialized_ = false;
 };
 
 // A helper class for receiving notifications from Kcer.
@@ -255,7 +263,8 @@
     }
   }
 
-  // Waits until the required number of notifications is received.
+  // Waits until the required number of notifications is received. Tries to
+  // check that no extra notifications is sent.
   bool WaitUntil(size_t notifications) {
     if (notifications_counter_ < notifications) {
       expected_notifications_ = notifications;
@@ -285,18 +294,6 @@
   base::WeakPtrFactory<NotificationsObserver> weak_factory_{this};
 };
 
-class KcerNssTest : public testing::Test {
- public:
-  KcerNssTest() : observer_(task_environment_) {}
-
- protected:
-  content::BrowserTaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME,
-      base::test::TaskEnvironment::MainThreadType::UI,
-      content::BrowserTaskEnvironment::REAL_IO_THREAD};
-  NotificationsObserver observer_;
-};
-
 std::unique_ptr<net::CertBuilder> MakeCertIssuer() {
   auto issuer = std::make_unique<net::CertBuilder>(/*orig_cert=*/nullptr,
                                                    /*issuer=*/nullptr);
@@ -320,137 +317,68 @@
   return cert_builder;
 }
 
-// Test that Kcer forwards notifications from external sources. (Notifications
-// created by Kcer are tested together with the methods that create them.)
-TEST_F(KcerNssTest, ObserveExternalNotification) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
+// Test fixture for KcerNss tests. Provides the least amount of pre-configured
+// setup to give more control to the tests themself.
+class KcerNssTest : public testing::Test {
+ public:
+  KcerNssTest() : observer_(task_environment_) {}
 
-  std::unique_ptr<Kcer> kcer =
-      internal::CreateKcer(IOTaskRunner(), user_token.GetWeakPtr(),
-                           /*device_token=*/nullptr);
+ protected:
+  void InitializeKcer(std::vector<Token> tokens) {
+    base::WeakPtr<internal::KcerToken> user_token_ptr;
+    base::WeakPtr<internal::KcerToken> device_token_ptr;
 
-  NotificationsObserver observer_1(task_environment_);
-  NotificationsObserver observer_2(task_environment_);
+    for (Token token_type : tokens) {
+      if (token_type == Token::kUser) {
+        CHECK(!user_token_ptr.MaybeValid());
+        user_token_ =
+            std::make_unique<TokenHolder>(token_type, /*initialize=*/true);
+        user_token_ptr = user_token_->GetWeakPtr();
+      } else if (token_type == Token::kDevice) {
+        CHECK(!device_token_ptr.MaybeValid());
+        device_token_ =
+            std::make_unique<TokenHolder>(token_type, /*initialize=*/true);
+        device_token_ptr = device_token_->GetWeakPtr();
+      }
+    }
 
-  // Add the first observer.
-  auto subscription_1 = kcer->AddObserver(observer_1.GetCallback());
+    kcer_ =
+        internal::CreateKcer(IOTaskRunner(), user_token_ptr, device_token_ptr);
+    observers_subscription_ = kcer_->AddObserver(observer_.GetCallback());
+  }
 
-  EXPECT_EQ(observer_1.Notifications(), 0u);
-
-  // Check that it receives a notification.
-  net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
-  EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/1));
-
-  // Add one more observer.
-  auto subscription_2 = kcer->AddObserver(observer_2.GetCallback());
-
-  // Check that both of them receive a notification.
-  net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
-  EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/2));
-  EXPECT_TRUE(observer_2.WaitUntil(/*notifications=*/1));
-
-  // Destroy the first subscription, the first observer should stop receiving
-  // notifications now.
-  subscription_1 = base::CallbackListSubscription();
-
-  // Check that only the second observer receives a notification.
-  net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
-  EXPECT_TRUE(observer_2.WaitUntil(/*notifications=*/2));
-  EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/2));
-}
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME,
+      base::test::TaskEnvironment::MainThreadType::UI,
+      content::BrowserTaskEnvironment::REAL_IO_THREAD};
+  NotificationsObserver observer_;
+  base::CallbackListSubscription observers_subscription_;
+  std::unique_ptr<TokenHolder> user_token_;
+  std::unique_ptr<TokenHolder> device_token_;
+  std::unique_ptr<Kcer> kcer_;
+};
 
 // Test that if a method is called with a token that is not (and won't be)
 // available, then an error is returned.
 TEST_F(KcerNssTest, UseUnavailableTokenThenGetError) {
-  std::unique_ptr<Kcer> kcer =
-      internal::CreateKcer(IOTaskRunner(), /*user_token=*/nullptr,
-                           /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer(/*tokens=*/{});
 
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                       /*hardware_backed=*/true, generate_waiter.GetCallback());
+  kcer_->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
+                        /*hardware_backed=*/true,
+                        generate_waiter.GetCallback());
 
   ASSERT_FALSE(generate_waiter.Get().has_value());
   EXPECT_EQ(generate_waiter.Get().error(), Error::kTokenIsNotAvailable);
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
-TEST_F(KcerNssTest, ImportCertForImportedKey) {
-  absl::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
-      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
-  ASSERT_TRUE(key.has_value() && (key->size() > 0));
-  absl::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
-      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
-  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
-
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer =
-      internal::CreateKcer(IOTaskRunner(), user_token.GetWeakPtr(),
-                           /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
-
-  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
-  kcer->ImportKey(Token::kUser, Pkcs8PrivateKeyInfoDer(std::move(key.value())),
-                  import_key_waiter.GetCallback());
-  ASSERT_TRUE(import_key_waiter.Get().has_value());
-
-  const PublicKey& public_key = import_key_waiter.Get().value();
-
-  EXPECT_EQ(public_key.GetToken(), Token::kUser);
-  // Arbitrary bytes, not much to check about them.
-  EXPECT_EQ(public_key.GetPkcs11Id()->size(), 20u);
-  // Arbitrary bytes, not much to check about them.
-  EXPECT_EQ(public_key.GetSpki()->size(), 294u);
-
-  base::test::TestFuture<base::expected<void, Error>> import_cert_waiter;
-  kcer->ImportCertFromBytes(Token::kUser, CertDer(std::move(cert.value())),
-                            import_cert_waiter.GetCallback());
-  EXPECT_TRUE(import_cert_waiter.Get().has_value());
-
-  EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
-}
-
-// Test that a certificate can not be imported, if there's no key for it on
-// the token.
-TEST_F(KcerNssTest, ImportCertWithoutKeyThenFail) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
-
-  std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
-  std::unique_ptr<net::CertBuilder> cert_builder = MakeCertBuilder(
-      issuer.get(), base::Base64Decode(kPublicKeyBase64).value());
-
-  CertDer cert(StrToBytes(cert_builder->GetDER()));
-
-  base::test::TestFuture<base::expected<void, Error>> import_waiter;
-  kcer->ImportCertFromBytes(Token::kUser, std::move(cert),
-                            import_waiter.GetCallback());
-  ASSERT_FALSE(import_waiter.Get().has_value());
-  EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToImportCertificate);
-
-  // Double check that ListCerts doesn't find the cert.
-  base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
-                         base::flat_map<Token, Error>>
-      certs_waiter;
-  kcer->ListCerts({Token::kUser}, certs_waiter.GetCallback());
-  EXPECT_TRUE(certs_waiter.Get<0>().empty());  // Cert list is empty.
-  EXPECT_TRUE(certs_waiter.Get<1>().empty());  // Error map is empty.
-  EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
-}
-
 // Test that all methods can be queued while a token is initializing and that
 // the entire task queue can be processed when initialization completes (in
 // this case - completes with a failure).
-TEST_F(KcerNssTest, QueueTasksFailInitializationThenGetErrors) {
-  TokenHolder user_token(Token::kUser);
+TEST_F(KcerNssTest, QueueTasksThenFailInitializationThenGetErrors) {
+  // Do not initialize yet to simulate slow initialization.
+  TokenHolder user_token(Token::kUser, /*initialize=*/false);
 
   std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
   std::unique_ptr<net::CertBuilder> cert_builder = MakeCertBuilder(
@@ -590,15 +518,47 @@
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
-TEST_F(KcerNssTest, ListKeys) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-  TokenHolder device_token(Token::kDevice);
-  device_token.Initialize();
+// Test that Kcer forwards notifications from external sources. (Notifications
+// created by Kcer are tested together with the methods that create them.)
+TEST_F(KcerNssTest, ObserveExternalNotification) {
+  TokenHolder user_token(Token::kUser, /*initialize=*/true);
 
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), device_token.GetWeakPtr());
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  std::unique_ptr<Kcer> kcer =
+      internal::CreateKcer(IOTaskRunner(), user_token.GetWeakPtr(),
+                           /*device_token=*/nullptr);
+
+  NotificationsObserver observer_1(task_environment_);
+  NotificationsObserver observer_2(task_environment_);
+
+  // Add the first observer.
+  auto subscription_1 = kcer->AddObserver(observer_1.GetCallback());
+
+  EXPECT_EQ(observer_1.Notifications(), 0u);
+
+  // Check that it receives a notification.
+  net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
+  EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/1));
+
+  // Add one more observer.
+  auto subscription_2 = kcer->AddObserver(observer_2.GetCallback());
+
+  // Check that both of them receive a notification.
+  net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
+  EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/2));
+  EXPECT_TRUE(observer_2.WaitUntil(/*notifications=*/1));
+
+  // Destroy the first subscription, the first observer should stop receiving
+  // notifications now.
+  subscription_1 = base::CallbackListSubscription();
+
+  // Check that only the second observer receives a notification.
+  net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
+  EXPECT_TRUE(observer_2.WaitUntil(/*notifications=*/2));
+  EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/2));
+}
+
+TEST_F(KcerNssTest, ListKeys) {
+  InitializeKcer({Token::kUser, Token::kDevice});
 
   std::vector<PublicKey> all_expected_keys;
   std::vector<PublicKey> user_expected_keys;
@@ -608,8 +568,8 @@
   {
     base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
         list_keys_waiter;
-    kcer->ListKeys({Token::kUser, Token::kDevice},
-                   list_keys_waiter.GetCallback());
+    kcer_->ListKeys({Token::kUser, Token::kDevice},
+                    list_keys_waiter.GetCallback());
 
     ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
     EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
@@ -620,9 +580,9 @@
   {
     base::test::TestFuture<base::expected<PublicKey, Error>>
         generate_key_waiter;
-    kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                         /*hardware_backed=*/true,
-                         generate_key_waiter.GetCallback());
+    kcer_->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
+                          /*hardware_backed=*/true,
+                          generate_key_waiter.GetCallback());
     ASSERT_TRUE(generate_key_waiter.Get().has_value());
     user_expected_keys.push_back(generate_key_waiter.Get().value());
     all_expected_keys.push_back(generate_key_waiter.Take().value());
@@ -632,8 +592,8 @@
   {
     base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
         list_keys_waiter;
-    kcer->ListKeys({Token::kUser, Token::kDevice},
-                   list_keys_waiter.GetCallback());
+    kcer_->ListKeys({Token::kUser, Token::kDevice},
+                    list_keys_waiter.GetCallback());
     ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
     EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
                 testing::UnorderedElementsAreArray(all_expected_keys));
@@ -643,9 +603,9 @@
   {
     base::test::TestFuture<base::expected<PublicKey, Error>>
         generate_key_waiter;
-    kcer->GenerateRsaKey(Token::kDevice, /*modulus_length_bits=*/2048,
-                         /*hardware_backed=*/true,
-                         generate_key_waiter.GetCallback());
+    kcer_->GenerateRsaKey(Token::kDevice, /*modulus_length_bits=*/2048,
+                          /*hardware_backed=*/true,
+                          generate_key_waiter.GetCallback());
     ASSERT_TRUE(generate_key_waiter.Get().has_value());
     device_expected_keys.push_back(generate_key_waiter.Get().value());
     all_expected_keys.push_back(generate_key_waiter.Take().value());
@@ -655,8 +615,8 @@
   {
     base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
         list_keys_waiter;
-    kcer->ListKeys({Token::kUser, Token::kDevice},
-                   list_keys_waiter.GetCallback());
+    kcer_->ListKeys({Token::kUser, Token::kDevice},
+                    list_keys_waiter.GetCallback());
     ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
     EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
                 testing::UnorderedElementsAreArray(all_expected_keys));
@@ -666,9 +626,9 @@
   {
     base::test::TestFuture<base::expected<PublicKey, Error>>
         generate_key_waiter;
-    kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
-                        /*hardware_backed=*/true,
-                        generate_key_waiter.GetCallback());
+    kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
+                         /*hardware_backed=*/true,
+                         generate_key_waiter.GetCallback());
     ASSERT_TRUE(generate_key_waiter.Get().has_value());
     user_expected_keys.push_back(generate_key_waiter.Get().value());
     all_expected_keys.push_back(generate_key_waiter.Take().value());
@@ -678,9 +638,9 @@
   {
     base::test::TestFuture<base::expected<PublicKey, Error>>
         generate_key_waiter;
-    kcer->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
-                        /*hardware_backed=*/true,
-                        generate_key_waiter.GetCallback());
+    kcer_->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
+                         /*hardware_backed=*/true,
+                         generate_key_waiter.GetCallback());
     ASSERT_TRUE(generate_key_waiter.Get().has_value());
     device_expected_keys.push_back(generate_key_waiter.Get().value());
     all_expected_keys.push_back(generate_key_waiter.Take().value());
@@ -690,8 +650,8 @@
   {
     base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
         list_keys_waiter;
-    kcer->ListKeys({Token::kUser, Token::kDevice},
-                   list_keys_waiter.GetCallback());
+    kcer_->ListKeys({Token::kUser, Token::kDevice},
+                    list_keys_waiter.GetCallback());
     ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
     EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
                 testing::UnorderedElementsAreArray(all_expected_keys));
@@ -701,7 +661,7 @@
   {
     base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
         list_keys_waiter;
-    kcer->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
+    kcer_->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
     ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
     EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
                 testing::UnorderedElementsAreArray(user_expected_keys));
@@ -711,7 +671,7 @@
   {
     base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
         list_keys_waiter;
-    kcer->ListKeys({Token::kDevice}, list_keys_waiter.GetCallback());
+    kcer_->ListKeys({Token::kDevice}, list_keys_waiter.GetCallback());
     ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
     EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
                 testing::UnorderedElementsAreArray(device_expected_keys));
@@ -725,17 +685,12 @@
 // TODO(miersh): Expand crypto::SignatureVerifier to work with more signature
 // schemes and add them to the test.
 TEST_F(KcerNssTest, SignRsa) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_key_waiter;
-  kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                       /*hardware_backed=*/true,
-                       generate_key_waiter.GetCallback());
+  kcer_->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
+                        /*hardware_backed=*/true,
+                        generate_key_waiter.GetCallback());
   ASSERT_TRUE(generate_key_waiter.Get().has_value());
   const PublicKey& public_key = generate_key_waiter.Get().value();
 
@@ -744,8 +699,8 @@
   // Test kRsaPkcs1Sha1 signature.
   {
     base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
-    kcer->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha1,
-               data_to_sign, sign_waiter.GetCallback());
+    kcer_->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha1,
+                data_to_sign, sign_waiter.GetCallback());
     ASSERT_TRUE(sign_waiter.Get().has_value());
     const Signature& signature = sign_waiter.Get().value();
 
@@ -757,18 +712,19 @@
     EXPECT_TRUE(signature_verifier.VerifyFinal());
   }
 
-  // Test kRsaPkcs1Sha256 signature.
+  // Test kRsaPkcs1Sha256 signature. Save signature to compare with it later.
+  Signature rsa256_signature;
   {
     base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
-    kcer->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha256,
-               data_to_sign, sign_waiter.GetCallback());
+    kcer_->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha256,
+                data_to_sign, sign_waiter.GetCallback());
     ASSERT_TRUE(sign_waiter.Get().has_value());
-    const Signature& signature = sign_waiter.Get().value();
+    rsa256_signature = sign_waiter.Get().value();
 
     crypto::SignatureVerifier signature_verifier;
     ASSERT_TRUE(signature_verifier.VerifyInit(
         crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
-        signature.value(), public_key.GetSpki().value()));
+        rsa256_signature.value(), public_key.GetSpki().value()));
     signature_verifier.VerifyUpdate(data_to_sign.value());
     EXPECT_TRUE(signature_verifier.VerifyFinal());
   }
@@ -776,8 +732,8 @@
   // Test kRsaPssRsaeSha256 signature.
   {
     base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
-    kcer->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPssRsaeSha256,
-               data_to_sign, sign_waiter.GetCallback());
+    kcer_->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPssRsaeSha256,
+                data_to_sign, sign_waiter.GetCallback());
     ASSERT_TRUE(sign_waiter.Get().has_value());
     const Signature& signature = sign_waiter.Get().value();
 
@@ -789,6 +745,37 @@
     EXPECT_TRUE(signature_verifier.VerifyFinal());
   }
 
+  // Test `Kcer::SignRsaPkcs1Raw()` (kRsaPkcs1Sha256, but for pre-hashed
+  // values).
+  {
+    // A caller would need to hash the data themself before calling
+    // `SignRsaPkcs1Digest`, do that here.
+    auto hasher = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
+    hasher->Update(data_to_sign->data(), data_to_sign->size());
+    std::vector<uint8_t> hash(hasher->GetHashLength());
+    hasher->Finish(hash.data(), hash.size());
+    DigestWithPrefix digest_with_prefix(PrependSHA256DigestInfo(hash));
+
+    // Generate the signature.
+    base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
+    kcer_->SignRsaPkcs1Raw(PrivateKeyHandle(public_key),
+                           std::move(digest_with_prefix),
+                           sign_waiter.GetCallback());
+    ASSERT_TRUE(sign_waiter.Get().has_value());
+    const Signature& signature = sign_waiter.Get().value();
+
+    // Verify the signature.
+    crypto::SignatureVerifier signature_verifier;
+    ASSERT_TRUE(signature_verifier.VerifyInit(
+        crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
+        signature.value(), public_key.GetSpki().value()));
+    signature_verifier.VerifyUpdate(data_to_sign.value());
+    EXPECT_TRUE(signature_verifier.VerifyFinal());
+    // Manual hashing + `SignRsaPkcs1Digest` should produce the same signature
+    // as just `Sign`.
+    EXPECT_EQ(signature, rsa256_signature);
+  }
+
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
@@ -796,17 +783,12 @@
 // TODO(miersh): Expand crypto::SignatureVerifier to work with more signature
 // schemes and add them to the test.
 TEST_F(KcerNssTest, SignEcc) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_key_waiter;
-  kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
-                      /*hardware_backed=*/true,
-                      generate_key_waiter.GetCallback());
+  kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
+                       /*hardware_backed=*/true,
+                       generate_key_waiter.GetCallback());
   ASSERT_TRUE(generate_key_waiter.Get().has_value());
   const PublicKey& public_key = generate_key_waiter.Get().value();
 
@@ -815,9 +797,9 @@
   // Test kEcdsaSecp256r1Sha256 signature.
   {
     base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
-    kcer->Sign(PrivateKeyHandle(public_key),
-               SigningScheme::kEcdsaSecp256r1Sha256, data_to_sign,
-               sign_waiter.GetCallback());
+    kcer_->Sign(PrivateKeyHandle(public_key),
+                SigningScheme::kEcdsaSecp256r1Sha256, data_to_sign,
+                sign_waiter.GetCallback());
     ASSERT_TRUE(sign_waiter.Get().has_value());
     const Signature& signature = sign_waiter.Get().value();
 
@@ -832,70 +814,40 @@
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
-// Test that `Kcer::SignRsaPkcs1Raw()` produces a correct signature.
-TEST_F(KcerNssTest, SignRsaPkcs1Raw) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
+// Test that a certificate can not be imported, if there's no key for it on
+// the token.
+TEST_F(KcerNssTest, ImportCertWithoutKeyThenFail) {
+  InitializeKcer({Token::kUser});
 
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
+  std::unique_ptr<net::CertBuilder> cert_builder = MakeCertBuilder(
+      issuer.get(), base::Base64Decode(kPublicKeyBase64).value());
 
-  base::test::TestFuture<base::expected<PublicKey, Error>> generate_key_waiter;
-  kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                       /*hardware_backed=*/true,
-                       generate_key_waiter.GetCallback());
-  ASSERT_TRUE(generate_key_waiter.Get().has_value());
-  const PublicKey& public_key = generate_key_waiter.Get().value();
+  CertDer cert(StrToBytes(cert_builder->GetDER()));
 
-  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+  base::test::TestFuture<base::expected<void, Error>> import_waiter;
+  kcer_->ImportCertFromBytes(Token::kUser, std::move(cert),
+                             import_waiter.GetCallback());
+  ASSERT_FALSE(import_waiter.Get().has_value());
+  EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToImportCertificate);
 
-  // A caller would need to hash the data themself before calling
-  // `SignRsaPkcs1Raw`, do that here.
-  auto hasher = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
-  hasher->Update(data_to_sign->data(), data_to_sign->size());
-  std::vector<uint8_t> hash(hasher->GetHashLength());
-  hasher->Finish(hash.data(), hash.size());
-  DigestWithPrefix digest_with_prefix(PrependSHA256DigestInfo(hash));
-
-  // Generate the signature.
-  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
-  kcer->SignRsaPkcs1Raw(PrivateKeyHandle(public_key),
-                        std::move(digest_with_prefix),
-                        sign_waiter.GetCallback());
-  ASSERT_TRUE(sign_waiter.Get().has_value());
-  const Signature& signature = sign_waiter.Get().value();
-
-  // Verify the signature.
-  crypto::SignatureVerifier signature_verifier;
-  ASSERT_TRUE(signature_verifier.VerifyInit(
-      crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
-      signature.value(), public_key.GetSpki().value()));
-  signature_verifier.VerifyUpdate(data_to_sign.value());
-  EXPECT_TRUE(signature_verifier.VerifyFinal());
-
-  // Verify that manual hashing + `SignRsaPkcs1Raw` produces the same
-  // signature as just `Sign`.
-  base::test::TestFuture<base::expected<Signature, Error>> normal_sign_waiter;
-  kcer->Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha256,
-             data_to_sign, normal_sign_waiter.GetCallback());
-  ASSERT_TRUE(normal_sign_waiter.Get().has_value());
-  EXPECT_EQ(sign_waiter.Get().value(), normal_sign_waiter.Get().value());
+  // Double check that ListCerts doesn't find the cert.
+  base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
+                         base::flat_map<Token, Error>>
+      certs_waiter;
+  kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
+  EXPECT_TRUE(certs_waiter.Get<0>().empty());  // Cert list is empty.
+  EXPECT_TRUE(certs_waiter.Get<1>().empty());  // Error map is empty.
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
 // Test that Kcer::GetTokenInfo() method returns meaningful values.
 TEST_F(KcerNssTest, GetTokenInfo) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   base::test::TestFuture<base::expected<TokenInfo, Error>>
       get_token_info_waiter;
-  kcer->GetTokenInfo(Token::kUser, get_token_info_waiter.GetCallback());
+  kcer_->GetTokenInfo(Token::kUser, get_token_info_waiter.GetCallback());
   ASSERT_TRUE(get_token_info_waiter.Get().has_value());
   const TokenInfo& token_info = get_token_info_waiter.Get().value();
 
@@ -911,22 +863,19 @@
 
 // Test RSA specific fields from GetKeyInfo's result.
 TEST_F(KcerNssTest, GetKeyInfoForRsaKey) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   // Generate new key.
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                       /*hardware_backed=*/true, generate_waiter.GetCallback());
+  kcer_->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
+                        /*hardware_backed=*/true,
+                        generate_waiter.GetCallback());
   ASSERT_TRUE(generate_waiter.Get().has_value());
   const PublicKey& public_key = generate_waiter.Get().value();
 
   base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
-  kcer->GetKeyInfo(PrivateKeyHandle(public_key), key_info_waiter.GetCallback());
+  kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
+                    key_info_waiter.GetCallback());
   ASSERT_TRUE(key_info_waiter.Get().has_value());
   const KeyInfo& key_info = key_info_waiter.Get().value();
   EXPECT_EQ(key_info.key_type, KeyType::kRsa);
@@ -942,22 +891,18 @@
 
 // Test ECC specific fields from GetKeyInfo's result.
 TEST_F(KcerNssTest, GetKeyInfoForEccKey) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   // Generate new key.
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
-                      /*hardware_backed=*/true, generate_waiter.GetCallback());
+  kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
+                       /*hardware_backed=*/true, generate_waiter.GetCallback());
   ASSERT_TRUE(generate_waiter.Get().has_value());
   const PublicKey& public_key = generate_waiter.Get().value();
 
   base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
-  kcer->GetKeyInfo(PrivateKeyHandle(public_key), key_info_waiter.GetCallback());
+  kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
+                    key_info_waiter.GetCallback());
   ASSERT_TRUE(key_info_waiter.Get().has_value());
   const KeyInfo& key_info = key_info_waiter.Get().value();
   EXPECT_EQ(key_info.key_type, KeyType::kEcc);
@@ -971,17 +916,12 @@
 // Test generic fields from GetKeyInfo's result and they get updated after
 // related Set* methods.
 TEST_F(KcerNssTest, GetKeyInfoGeneric) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   // Generate new key.
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
-                      /*hardware_backed=*/true, generate_waiter.GetCallback());
+  kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
+                       /*hardware_backed=*/true, generate_waiter.GetCallback());
   ASSERT_TRUE(generate_waiter.Get().has_value());
   const PublicKey& public_key = generate_waiter.Get().value();
 
@@ -999,8 +939,8 @@
 
   {
     base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
-    kcer->GetKeyInfo(PrivateKeyHandle(public_key),
-                     key_info_waiter.GetCallback());
+    kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
+                      key_info_waiter.GetCallback());
     ASSERT_TRUE(key_info_waiter.Get().has_value());
     const KeyInfo& key_info = key_info_waiter.Get().value();
 
@@ -1017,16 +957,16 @@
     expected_key_info.nickname = "new_nickname";
 
     base::test::TestFuture<base::expected<void, Error>> set_nickname_waiter;
-    kcer->SetKeyNickname(PrivateKeyHandle(public_key),
-                         expected_key_info.nickname.value(),
-                         set_nickname_waiter.GetCallback());
+    kcer_->SetKeyNickname(PrivateKeyHandle(public_key),
+                          expected_key_info.nickname.value(),
+                          set_nickname_waiter.GetCallback());
     ASSERT_TRUE(set_nickname_waiter.Get().has_value());
   }
 
   {
     base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
-    kcer->GetKeyInfo(PrivateKeyHandle(public_key),
-                     key_info_waiter.GetCallback());
+    kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
+                      key_info_waiter.GetCallback());
     ASSERT_TRUE(key_info_waiter.Get().has_value());
     EXPECT_TRUE(
         KeyInfoEquals(expected_key_info, key_info_waiter.Get().value()));
@@ -1038,16 +978,16 @@
     expected_key_info.key_permissions->mutable_key_usages()->set_arc(true);
 
     base::test::TestFuture<base::expected<void, Error>> set_permissions_waiter;
-    kcer->SetKeyPermissions(PrivateKeyHandle(public_key),
-                            expected_key_info.key_permissions.value(),
-                            set_permissions_waiter.GetCallback());
+    kcer_->SetKeyPermissions(PrivateKeyHandle(public_key),
+                             expected_key_info.key_permissions.value(),
+                             set_permissions_waiter.GetCallback());
     ASSERT_TRUE(set_permissions_waiter.Get().has_value());
   }
 
   {
     base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
-    kcer->GetKeyInfo(PrivateKeyHandle(public_key),
-                     key_info_waiter.GetCallback());
+    kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
+                      key_info_waiter.GetCallback());
     ASSERT_TRUE(key_info_waiter.Get().has_value());
     EXPECT_TRUE(
         KeyInfoEquals(expected_key_info, key_info_waiter.Get().value()));
@@ -1057,7 +997,7 @@
     expected_key_info.cert_provisioning_profile_id = "cert_prov_id_123";
 
     base::test::TestFuture<base::expected<void, Error>> set_permissions_waiter;
-    kcer->SetCertProvisioningProfileId(
+    kcer_->SetCertProvisioningProfileId(
         PrivateKeyHandle(public_key),
         expected_key_info.cert_provisioning_profile_id.value(),
         set_permissions_waiter.GetCallback());
@@ -1066,8 +1006,8 @@
 
   {
     base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
-    kcer->GetKeyInfo(PrivateKeyHandle(public_key),
-                     key_info_waiter.GetCallback());
+    kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
+                      key_info_waiter.GetCallback());
     ASSERT_TRUE(key_info_waiter.Get().has_value());
     EXPECT_TRUE(
         KeyInfoEquals(expected_key_info, key_info_waiter.Get().value()));
@@ -1076,27 +1016,53 @@
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
-// Test different ways to call DoesPrivateKeyExist() method and that it returns
-// correct results when Kcer has access to one token.
-TEST_F(KcerNssTest, DoesPrivateKeyExistOneToken) {
-  TokenHolder device_token(Token::kDevice);
-  device_token.Initialize();
+TEST_F(KcerNssTest, ImportCertForImportedKey) {
+  InitializeKcer({Token::kUser});
 
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), /*user_token=*/nullptr, device_token.GetWeakPtr());
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  absl::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
+      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
+  ASSERT_TRUE(key.has_value() && (key->size() > 0));
+  absl::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
+      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
+  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
+
+  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
+  kcer_->ImportKey(Token::kUser, Pkcs8PrivateKeyInfoDer(std::move(key.value())),
+                   import_key_waiter.GetCallback());
+  ASSERT_TRUE(import_key_waiter.Get().has_value());
+
+  const PublicKey& public_key = import_key_waiter.Get().value();
+
+  EXPECT_EQ(public_key.GetToken(), Token::kUser);
+  // Arbitrary bytes, not much to check about them.
+  EXPECT_EQ(public_key.GetPkcs11Id()->size(), 20u);
+  // Arbitrary bytes, not much to check about them.
+  EXPECT_EQ(public_key.GetSpki()->size(), 294u);
+
+  base::test::TestFuture<base::expected<void, Error>> import_cert_waiter;
+  kcer_->ImportCertFromBytes(Token::kUser, CertDer(std::move(cert.value())),
+                             import_cert_waiter.GetCallback());
+  EXPECT_TRUE(import_cert_waiter.Get().has_value());
+
+  EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
+}
+
+// Test different ways to call DoesPrivateKeyExist() method and that it
+// returns correct results when Kcer has access to one token.
+TEST_F(KcerNssTest, DoesPrivateKeyExistOneToken) {
+  InitializeKcer({Token::kDevice});
 
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  kcer->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
-                      /*hardware_backed=*/true, generate_waiter.GetCallback());
+  kcer_->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
+                       /*hardware_backed=*/true, generate_waiter.GetCallback());
   ASSERT_TRUE(generate_waiter.Get().has_value());
   const PublicKey& public_key = generate_waiter.Get().value();
 
   // The private key should be found by the PublicKey.
   {
     base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(PrivateKeyHandle(public_key),
-                              does_exist_waiter.GetCallback());
+    kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key),
+                               does_exist_waiter.GetCallback());
     ASSERT_TRUE(does_exist_waiter.Get().has_value());
     EXPECT_EQ(does_exist_waiter.Get().value(), true);
   }
@@ -1104,8 +1070,8 @@
   // The private key should be found by the SPKI.
   {
     base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(PrivateKeyHandle(public_key.GetSpki()),
-                              does_exist_waiter.GetCallback());
+    kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key.GetSpki()),
+                               does_exist_waiter.GetCallback());
     ASSERT_TRUE(does_exist_waiter.Get().has_value());
     EXPECT_EQ(does_exist_waiter.Get().value(), true);
   }
@@ -1113,7 +1079,7 @@
   // The private key should be found on the specified token by the SPKI.
   {
     base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
+    kcer_->DoesPrivateKeyExist(
         PrivateKeyHandle(Token::kDevice, public_key.GetSpki()),
         does_exist_waiter.GetCallback());
     ASSERT_TRUE(does_exist_waiter.Get().has_value());
@@ -1123,7 +1089,7 @@
   // Looking for a key on a non-existing token should return an error.
   {
     base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
+    kcer_->DoesPrivateKeyExist(
         PrivateKeyHandle(Token::kUser, public_key.GetSpki()),
         does_exist_waiter.GetCallback());
     ASSERT_FALSE(does_exist_waiter.Get().has_value());
@@ -1133,7 +1099,7 @@
   // Looking for a key by an invalid SPKI should return an error.
   {
     base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
+    kcer_->DoesPrivateKeyExist(
         PrivateKeyHandle(PublicKeySpki(std::vector<uint8_t>{1, 2, 3})),
         does_exist_waiter.GetCallback());
     ASSERT_FALSE(does_exist_waiter.Get().has_value());
@@ -1145,7 +1111,7 @@
     std::vector<uint8_t> non_existing_key =
         base::Base64Decode(kPublicKeyBase64).value();
     base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
+    kcer_->DoesPrivateKeyExist(
         PrivateKeyHandle(PublicKeySpki(std::move(non_existing_key))),
         does_exist_waiter.GetCallback());
     ASSERT_TRUE(does_exist_waiter.Get().has_value())
@@ -1156,132 +1122,18 @@
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
 }
 
-class KcerNssAllKeyTypesTest : public KcerNssTest,
-                               public testing::WithParamInterface<KeyType> {
- protected:
-  KeyType GetKeyType() { return GetParam(); }
-};
-
-// Test different ways to call DoesPrivateKeyExist() method and that it returns
-// correct results when Kcer has access to two tokens.
-TEST_P(KcerNssAllKeyTypesTest, DoesPrivateKeyExistTwoTokens) {
-  TokenHolder device_token(Token::kDevice);
-  device_token.Initialize();
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), device_token.GetWeakPtr());
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
-
-  base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  switch (GetKeyType()) {
-    case KeyType::kRsa:
-      kcer->GenerateRsaKey(Token::kDevice, /*modulus_length_bits=*/2048,
-                           /*hardware_backed=*/true,
-                           generate_waiter.GetCallback());
-      break;
-    case KeyType::kEcc:
-      kcer->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
-                          /*hardware_backed=*/true,
-                          generate_waiter.GetCallback());
-      break;
-  }
-  ASSERT_TRUE(generate_waiter.Get().has_value());
-  const PublicKey& public_key = generate_waiter.Get().value();
-
-  // The private key should be found by the PublicKey.
-  {
-    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(PrivateKeyHandle(public_key),
-                              does_exist_waiter.GetCallback());
-    ASSERT_TRUE(does_exist_waiter.Get().has_value());
-    EXPECT_EQ(does_exist_waiter.Get().value(), true);
-  }
-
-  // The private key should be found by the SPKI.
-  {
-    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(PrivateKeyHandle(public_key.GetSpki()),
-                              does_exist_waiter.GetCallback());
-    ASSERT_TRUE(does_exist_waiter.Get().has_value());
-    EXPECT_EQ(does_exist_waiter.Get().value(), true);
-  }
-
-  // The private key should be found on the specified token by the SPKI.
-  {
-    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
-        PrivateKeyHandle(Token::kDevice, public_key.GetSpki()),
-        does_exist_waiter.GetCallback());
-    ASSERT_TRUE(does_exist_waiter.Get().has_value());
-    EXPECT_EQ(does_exist_waiter.Get().value(), true);
-  }
-
-  // Looking for a key on another (existing) token should return a negative
-  // result.
-  {
-    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
-        PrivateKeyHandle(Token::kUser, public_key.GetSpki()),
-        does_exist_waiter.GetCallback());
-    ASSERT_TRUE(does_exist_waiter.Get().has_value());
-    EXPECT_EQ(does_exist_waiter.Get().value(), false);
-  }
-
-  // Looking for a key by an incorrect SPKI should return an error.
-  {
-    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
-        PrivateKeyHandle(PublicKeySpki(std::vector<uint8_t>{1, 2, 3})),
-        does_exist_waiter.GetCallback());
-    ASSERT_FALSE(does_exist_waiter.Get().has_value());
-    EXPECT_EQ(does_exist_waiter.Get().error(), Error::kFailedToGetKeyId);
-  }
-
-  // Looking for a non-existing key should return a negative result.
-  {
-    std::vector<uint8_t> non_existing_key =
-        base::Base64Decode(kPublicKeyBase64).value();
-    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
-    kcer->DoesPrivateKeyExist(
-        PrivateKeyHandle(PublicKeySpki(std::move(non_existing_key))),
-        does_exist_waiter.GetCallback());
-    ASSERT_TRUE(does_exist_waiter.Get().has_value())
-        << does_exist_waiter.Get().error();
-    EXPECT_EQ(does_exist_waiter.Get().value(), false);
-  }
-
-  EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
-}
-
-TEST_P(KcerNssAllKeyTypesTest, RemoveKeyAndCertsWithManyCerts) {
+TEST_F(KcerNssTest, RemoveKeyAndCertsWithManyCerts) {
   if (NSS_VersionCheck("3.68") != PR_TRUE) {
     // TODO(b/283925148): Remove this when all the builders are updated.
     GTEST_SKIP() << "NSS is too old";
   }
 
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser});
 
   // Generate new key.
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
-  switch (GetKeyType()) {
-    case KeyType::kRsa:
-      kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                           /*hardware_backed=*/true,
-                           generate_waiter.GetCallback());
-      break;
-    case KeyType::kEcc:
-      kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
-                          /*hardware_backed=*/true,
-                          generate_waiter.GetCallback());
-      break;
-  }
+  kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
+                       /*hardware_backed=*/true, generate_waiter.GetCallback());
   ASSERT_TRUE(generate_waiter.Get().has_value());
   const PublicKey& public_key = generate_waiter.Get().value();
 
@@ -1292,8 +1144,8 @@
         MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
     // Import a cert.
     base::test::TestFuture<base::expected<void, Error>> import_waiter;
-    kcer->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
-                         import_waiter.GetCallback());
+    kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
+                          import_waiter.GetCallback());
     EXPECT_TRUE(import_waiter.Get().has_value());
     EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
   }
@@ -1303,8 +1155,8 @@
         MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
     // Import a cert.
     base::test::TestFuture<base::expected<void, Error>> import_waiter;
-    kcer->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
-                         import_waiter.GetCallback());
+    kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
+                          import_waiter.GetCallback());
     EXPECT_TRUE(import_waiter.Get().has_value());
     EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/2));
   }
@@ -1314,8 +1166,8 @@
         MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
     // Import a cert.
     base::test::TestFuture<base::expected<void, Error>> import_waiter;
-    kcer->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
-                         import_waiter.GetCallback());
+    kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
+                          import_waiter.GetCallback());
     EXPECT_TRUE(import_waiter.Get().has_value());
     EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/3));
   }
@@ -1324,14 +1176,14 @@
   base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
                          base::flat_map<Token, Error>>
       certs_waiter;
-  kcer->ListCerts({Token::kUser}, certs_waiter.GetCallback());
+  kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
   EXPECT_TRUE(certs_waiter.Get<1>().empty());  // Error map is empty.
   EXPECT_EQ(certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>().size(),
             3u);
 
   base::test::TestFuture<base::expected<void, Error>> remove_waiter;
-  kcer->RemoveKeyAndCerts(PrivateKeyHandle(public_key),
-                          remove_waiter.GetCallback());
+  kcer_->RemoveKeyAndCerts(PrivateKeyHandle(public_key),
+                           remove_waiter.GetCallback());
   EXPECT_TRUE(remove_waiter.Get().has_value());
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/4));
 
@@ -1339,7 +1191,7 @@
   base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
                          base::flat_map<Token, Error>>
       certs_waiter_2;
-  kcer->ListCerts({Token::kUser}, certs_waiter_2.GetCallback());
+  kcer_->ListCerts({Token::kUser}, certs_waiter_2.GetCallback());
   EXPECT_TRUE(certs_waiter_2.Get<1>().empty());  // Error map is empty.
   EXPECT_TRUE(
       certs_waiter_2.Get<std::vector<scoped_refptr<const Cert>>>().empty());
@@ -1347,33 +1199,113 @@
   // Check that the generated key cannot be found anymore.
   base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
       list_keys_waiter;
-  kcer->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
+  kcer_->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
   ASSERT_TRUE(list_keys_waiter.Get<1>().empty());  // Error map is empty.
   EXPECT_TRUE(list_keys_waiter.Get<std::vector<PublicKey>>().empty());
 }
 
+class KcerNssAllKeyTypesTest : public KcerNssTest,
+                               public testing::WithParamInterface<KeyType> {
+ public:
+  KeyType GetKeyType() { return GetParam(); }
+};
+
+// Test different ways to call DoesPrivateKeyExist() method and that it
+// returns correct results when Kcer has access to two tokens.
+TEST_P(KcerNssAllKeyTypesTest, DoesPrivateKeyExistTwoTokens) {
+  InitializeKcer({Token::kUser, Token::kDevice});
+
+  base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
+  switch (GetKeyType()) {
+    case KeyType::kRsa:
+      kcer_->GenerateRsaKey(Token::kDevice, /*modulus_length_bits=*/2048,
+                            /*hardware_backed=*/true,
+                            generate_waiter.GetCallback());
+      break;
+    case KeyType::kEcc:
+      kcer_->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
+                           /*hardware_backed=*/true,
+                           generate_waiter.GetCallback());
+      break;
+  }
+  ASSERT_TRUE(generate_waiter.Get().has_value());
+  const PublicKey& public_key = generate_waiter.Get().value();
+
+  {
+    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
+    kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key),
+                               does_exist_waiter.GetCallback());
+    ASSERT_TRUE(does_exist_waiter.Get().has_value());
+    EXPECT_EQ(does_exist_waiter.Get().value(), true);
+  }
+
+  {
+    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
+    kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key.GetSpki()),
+                               does_exist_waiter.GetCallback());
+    ASSERT_TRUE(does_exist_waiter.Get().has_value());
+    EXPECT_EQ(does_exist_waiter.Get().value(), true);
+  }
+
+  {
+    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
+    kcer_->DoesPrivateKeyExist(
+        PrivateKeyHandle(Token::kDevice, public_key.GetSpki()),
+        does_exist_waiter.GetCallback());
+    ASSERT_TRUE(does_exist_waiter.Get().has_value());
+    EXPECT_EQ(does_exist_waiter.Get().value(), true);
+  }
+
+  {
+    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
+    kcer_->DoesPrivateKeyExist(
+        PrivateKeyHandle(Token::kUser, public_key.GetSpki()),
+        does_exist_waiter.GetCallback());
+    ASSERT_TRUE(does_exist_waiter.Get().has_value());
+    EXPECT_EQ(does_exist_waiter.Get().value(), false);
+  }
+
+  {
+    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
+    kcer_->DoesPrivateKeyExist(
+        PrivateKeyHandle(PublicKeySpki(std::vector<uint8_t>{1, 2, 3})),
+        does_exist_waiter.GetCallback());
+    ASSERT_FALSE(does_exist_waiter.Get().has_value());
+    EXPECT_EQ(does_exist_waiter.Get().error(), Error::kFailedToGetKeyId);
+  }
+
+  {
+    std::vector<uint8_t> non_existing_key =
+        base::Base64Decode(kPublicKeyBase64).value();
+    base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
+    kcer_->DoesPrivateKeyExist(
+        PrivateKeyHandle(PublicKeySpki(std::move(non_existing_key))),
+        does_exist_waiter.GetCallback());
+    ASSERT_TRUE(does_exist_waiter.Get().has_value())
+        << does_exist_waiter.Get().error();
+    EXPECT_EQ(does_exist_waiter.Get().value(), false);
+  }
+
+  EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
+}
+
 // Test that all methods work together as expected. Simulate a potential
 // lifecycle of a key and related objects.
 TEST_P(KcerNssAllKeyTypesTest, AllMethodsTogether) {
-  TokenHolder user_token(Token::kUser);
-  user_token.Initialize();
-
-  std::unique_ptr<Kcer> kcer = internal::CreateKcer(
-      IOTaskRunner(), user_token.GetWeakPtr(), /*device_token=*/nullptr);
-  auto subscription = kcer->AddObserver(observer_.GetCallback());
+  InitializeKcer({Token::kUser, Token::kDevice});
 
   // Generate new key.
   base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
   switch (GetKeyType()) {
     case KeyType::kRsa:
-      kcer->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
-                           /*hardware_backed=*/true,
-                           generate_waiter.GetCallback());
+      kcer_->GenerateRsaKey(Token::kUser, /*modulus_length_bits=*/2048,
+                            /*hardware_backed=*/true,
+                            generate_waiter.GetCallback());
       break;
     case KeyType::kEcc:
-      kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
-                          /*hardware_backed=*/true,
-                          generate_waiter.GetCallback());
+      kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
+                           /*hardware_backed=*/true,
+                           generate_waiter.GetCallback());
       break;
   }
   ASSERT_TRUE(generate_waiter.Get().has_value());
@@ -1385,8 +1317,8 @@
 
   // Import a cert for the key.
   base::test::TestFuture<base::expected<void, Error>> import_waiter;
-  kcer->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
-                       import_waiter.GetCallback());
+  kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
+                        import_waiter.GetCallback());
   EXPECT_TRUE(import_waiter.Get().has_value());
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
 
@@ -1394,7 +1326,7 @@
   base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
                          base::flat_map<Token, Error>>
       certs_waiter;
-  kcer->ListCerts({Token::kUser}, certs_waiter.GetCallback());
+  kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
   EXPECT_TRUE(certs_waiter.Get<1>().empty());  // Error map is empty.
   const auto& certs =
       certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>();
@@ -1404,7 +1336,7 @@
 
   // Remove the cert.
   base::test::TestFuture<base::expected<void, Error>> remove_cert_waiter;
-  kcer->RemoveCert(certs.front(), remove_cert_waiter.GetCallback());
+  kcer_->RemoveCert(certs.front(), remove_cert_waiter.GetCallback());
   ASSERT_TRUE(remove_cert_waiter.Get().has_value());
   EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/2));
 
@@ -1412,7 +1344,7 @@
   base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
                          base::flat_map<Token, Error>>
       certs_waiter_2;
-  kcer->ListCerts({Token::kUser}, certs_waiter_2.GetCallback());
+  kcer_->ListCerts({Token::kUser}, certs_waiter_2.GetCallback());
   EXPECT_TRUE(certs_waiter_2.Get<1>().empty());  // Error map is empty.
   ASSERT_EQ(certs_waiter_2.Get<std::vector<scoped_refptr<const Cert>>>().size(),
             0u);
@@ -1424,8 +1356,8 @@
   // Import another cert for the key to check that the key was not removed and
   // is still usable.
   base::test::TestFuture<base::expected<void, Error>> import_waiter_2;
-  kcer->ImportX509Cert(Token::kUser, cert_builder_2->GetX509Certificate(),
-                       import_waiter_2.GetCallback());
+  kcer_->ImportX509Cert(Token::kUser, cert_builder_2->GetX509Certificate(),
+                        import_waiter_2.GetCallback());
   EXPECT_TRUE(import_waiter_2.Get().has_value());
 }
 
diff --git a/chrome/browser/password_manager/android/cred_man_controller.cc b/chrome/browser/password_manager/android/cred_man_controller.cc
index 0e76509..43296970 100644
--- a/chrome/browser/password_manager/android/cred_man_controller.cc
+++ b/chrome/browser/password_manager/android/cred_man_controller.cc
@@ -29,7 +29,7 @@
         [](base::WeakPtr<password_manager::PasswordManagerDriver> driver,
            bool success) {
           driver->KeyboardReplacingSurfaceClosed(
-              PasswordManagerDriver::ToShowVirtualKeyboard(!success));
+              PasswordManagerDriver::ShowVirtualKeyboard(!success));
         },
         driver->AsWeakPtr()));
     cred_man_delegate->TriggerFullRequest();
diff --git a/chrome/browser/password_manager/android/cred_man_controller_unittest.cc b/chrome/browser/password_manager/android/cred_man_controller_unittest.cc
index c2ee0f4..20feb9a6 100644
--- a/chrome/browser/password_manager/android/cred_man_controller_unittest.cc
+++ b/chrome/browser/password_manager/android/cred_man_controller_unittest.cc
@@ -66,7 +66,8 @@
 TEST_F(CredManControllerTest, DoesNotShowIfNoResults) {
   base::test::ScopedFeatureList enable_feature(device::kWebAuthnAndroidCredMan);
 
-  base::MockRepeatingClosure mock_full_assertion_request;
+  base::MockCallback<base::RepeatingCallback<void(bool)>>
+      mock_full_assertion_request;
   web_authn_cred_man_delegate()->OnCredManConditionalRequestPending(
       /*render_frame_host=*/nullptr, /*has_results=*/false,
       mock_full_assertion_request.Get());
@@ -80,7 +81,8 @@
   }
   base::test::ScopedFeatureList enable_feature(device::kWebAuthnAndroidCredMan);
 
-  base::MockRepeatingClosure mock_full_assertion_request;
+  base::MockCallback<base::RepeatingCallback<void(bool)>>
+      mock_full_assertion_request;
   web_authn_cred_man_delegate()->OnCredManConditionalRequestPending(
       /*render_frame_host=*/nullptr, /*has_results=*/true,
       mock_full_assertion_request.Get());
diff --git a/chrome/browser/permissions/notification_permission_review_service_factory.cc b/chrome/browser/permissions/notification_permission_review_service_factory.cc
index 33e54b2..ac3b807 100644
--- a/chrome/browser/permissions/notification_permission_review_service_factory.cc
+++ b/chrome/browser/permissions/notification_permission_review_service_factory.cc
@@ -4,14 +4,16 @@
 
 #include "chrome/browser/permissions/notification_permission_review_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 
 // static
 NotificationPermissionsReviewServiceFactory*
 NotificationPermissionsReviewServiceFactory::GetInstance() {
-  return base::Singleton<NotificationPermissionsReviewServiceFactory>::get();
+  static base::NoDestructor<NotificationPermissionsReviewServiceFactory>
+      instance;
+  return instance.get();
 }
 
 // static
diff --git a/chrome/browser/permissions/notification_permission_review_service_factory.h b/chrome/browser/permissions/notification_permission_review_service_factory.h
index 030d59a..7cb0b2b 100644
--- a/chrome/browser/permissions/notification_permission_review_service_factory.h
+++ b/chrome/browser/permissions/notification_permission_review_service_factory.h
@@ -12,7 +12,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 
 namespace content {
@@ -34,8 +34,7 @@
       const NotificationPermissionsReviewServiceFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      NotificationPermissionsReviewServiceFactory>;
+  friend base::NoDestructor<NotificationPermissionsReviewServiceFactory>;
 
   NotificationPermissionsReviewServiceFactory();
   ~NotificationPermissionsReviewServiceFactory() override;
diff --git a/chrome/browser/permissions/notifications_engagement_service_factory.cc b/chrome/browser/permissions/notifications_engagement_service_factory.cc
index f170b69..40a20fcd 100644
--- a/chrome/browser/permissions/notifications_engagement_service_factory.cc
+++ b/chrome/browser/permissions/notifications_engagement_service_factory.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/permissions/notifications_engagement_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/permissions/notifications_engagement_service.h"
@@ -12,7 +12,8 @@
 // static
 NotificationsEngagementServiceFactory*
 NotificationsEngagementServiceFactory::GetInstance() {
-  return base::Singleton<NotificationsEngagementServiceFactory>::get();
+  static base::NoDestructor<NotificationsEngagementServiceFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/chrome/browser/permissions/notifications_engagement_service_factory.h b/chrome/browser/permissions/notifications_engagement_service_factory.h
index 9a0875b..fc22026 100644
--- a/chrome/browser/permissions/notifications_engagement_service_factory.h
+++ b/chrome/browser/permissions/notifications_engagement_service_factory.h
@@ -12,7 +12,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 
 namespace content {
@@ -34,8 +34,7 @@
       const NotificationsEngagementServiceFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      NotificationsEngagementServiceFactory>;
+  friend base::NoDestructor<NotificationsEngagementServiceFactory>;
 
   NotificationsEngagementServiceFactory();
   ~NotificationsEngagementServiceFactory() override;
diff --git a/chrome/browser/permissions/one_time_permissions_tracker_factory.cc b/chrome/browser/permissions/one_time_permissions_tracker_factory.cc
index 1e5d602..cab98b40 100644
--- a/chrome/browser/permissions/one_time_permissions_tracker_factory.cc
+++ b/chrome/browser/permissions/one_time_permissions_tracker_factory.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/permissions/one_time_permissions_tracker_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/permissions/one_time_permissions_tracker.h"
 #include "chrome/browser/profiles/profile.h"
 
@@ -17,7 +17,8 @@
 
 OneTimePermissionsTrackerFactory*
 OneTimePermissionsTrackerFactory::GetInstance() {
-  return base::Singleton<OneTimePermissionsTrackerFactory>::get();
+  static base::NoDestructor<OneTimePermissionsTrackerFactory> instance;
+  return instance.get();
 }
 
 OneTimePermissionsTrackerFactory::OneTimePermissionsTrackerFactory()
diff --git a/chrome/browser/permissions/one_time_permissions_tracker_factory.h b/chrome/browser/permissions/one_time_permissions_tracker_factory.h
index 4b1ee9ae..0f4990ae 100644
--- a/chrome/browser/permissions/one_time_permissions_tracker_factory.h
+++ b/chrome/browser/permissions/one_time_permissions_tracker_factory.h
@@ -9,7 +9,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 class OneTimePermissionsTracker;
 
@@ -28,7 +28,7 @@
   bool ServiceIsCreatedWithBrowserContext() const override;
 
  private:
-  friend struct base::DefaultSingletonTraits<OneTimePermissionsTrackerFactory>;
+  friend base::NoDestructor<OneTimePermissionsTrackerFactory>;
 
   OneTimePermissionsTrackerFactory();
   ~OneTimePermissionsTrackerFactory() override;
diff --git a/chrome/browser/permissions/origin_keyed_permission_action_service_factory.cc b/chrome/browser/permissions/origin_keyed_permission_action_service_factory.cc
index a3f5cb1..cd41fa2 100644
--- a/chrome/browser/permissions/origin_keyed_permission_action_service_factory.cc
+++ b/chrome/browser/permissions/origin_keyed_permission_action_service_factory.cc
@@ -18,7 +18,8 @@
 // static
 OriginKeyedPermissionActionServiceFactory*
 OriginKeyedPermissionActionServiceFactory::GetInstance() {
-  return base::Singleton<OriginKeyedPermissionActionServiceFactory>::get();
+  static base::NoDestructor<OriginKeyedPermissionActionServiceFactory> instance;
+  return instance.get();
 }
 
 OriginKeyedPermissionActionServiceFactory::
diff --git a/chrome/browser/permissions/origin_keyed_permission_action_service_factory.h b/chrome/browser/permissions/origin_keyed_permission_action_service_factory.h
index 55b2fae..ac6b82e1 100644
--- a/chrome/browser/permissions/origin_keyed_permission_action_service_factory.h
+++ b/chrome/browser/permissions/origin_keyed_permission_action_service_factory.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_ORIGIN_KEYED_PERMISSION_ACTION_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_PERMISSIONS_ORIGIN_KEYED_PERMISSION_ACTION_SERVICE_FACTORY_H_
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
 class Profile;
@@ -29,8 +29,7 @@
   static OriginKeyedPermissionActionServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      OriginKeyedPermissionActionServiceFactory>;
+  friend base::NoDestructor<OriginKeyedPermissionActionServiceFactory>;
   OriginKeyedPermissionActionServiceFactory();
 
   ~OriginKeyedPermissionActionServiceFactory() override;
diff --git a/chrome/browser/permissions/permission_actions_history_factory.cc b/chrome/browser/permissions/permission_actions_history_factory.cc
index dd14719..48848c2 100644
--- a/chrome/browser/permissions/permission_actions_history_factory.cc
+++ b/chrome/browser/permissions/permission_actions_history_factory.cc
@@ -17,7 +17,8 @@
 // static
 PermissionActionsHistoryFactory*
 PermissionActionsHistoryFactory::GetInstance() {
-  return base::Singleton<PermissionActionsHistoryFactory>::get();
+  static base::NoDestructor<PermissionActionsHistoryFactory> instance;
+  return instance.get();
 }
 
 PermissionActionsHistoryFactory::PermissionActionsHistoryFactory()
diff --git a/chrome/browser/permissions/permission_actions_history_factory.h b/chrome/browser/permissions/permission_actions_history_factory.h
index 4243903..c3df811 100644
--- a/chrome/browser/permissions/permission_actions_history_factory.h
+++ b/chrome/browser/permissions/permission_actions_history_factory.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_PERMISSION_ACTIONS_HISTORY_FACTORY_H_
 #define CHROME_BROWSER_PERMISSIONS_PERMISSION_ACTIONS_HISTORY_FACTORY_H_
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
 class Profile;
@@ -24,7 +24,7 @@
   static PermissionActionsHistoryFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<PermissionActionsHistoryFactory>;
+  friend base::NoDestructor<PermissionActionsHistoryFactory>;
 
   PermissionActionsHistoryFactory();
   ~PermissionActionsHistoryFactory() override;
diff --git a/chrome/browser/permissions/permission_auditing_service_factory.cc b/chrome/browser/permissions/permission_auditing_service_factory.cc
index c5a663b..71561544 100644
--- a/chrome/browser/permissions/permission_auditing_service_factory.cc
+++ b/chrome/browser/permissions/permission_auditing_service_factory.cc
@@ -6,7 +6,7 @@
 
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/after_startup_task_utils.h"
@@ -29,7 +29,8 @@
 // static
 PermissionAuditingServiceFactory*
 PermissionAuditingServiceFactory::GetInstance() {
-  return base::Singleton<PermissionAuditingServiceFactory>::get();
+  static base::NoDestructor<PermissionAuditingServiceFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/chrome/browser/permissions/permission_auditing_service_factory.h b/chrome/browser/permissions/permission_auditing_service_factory.h
index 540a89b3..c287710b 100644
--- a/chrome/browser/permissions/permission_auditing_service_factory.h
+++ b/chrome/browser/permissions/permission_auditing_service_factory.h
@@ -11,7 +11,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 
 namespace permissions {
@@ -38,7 +38,7 @@
       PermissionAuditingServiceFactory&&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<PermissionAuditingServiceFactory>;
+  friend base::NoDestructor<PermissionAuditingServiceFactory>;
 
   PermissionAuditingServiceFactory();
   ~PermissionAuditingServiceFactory() override;
diff --git a/chrome/browser/permissions/permission_context_base_permissions_policy_unittest.cc b/chrome/browser/permissions/permission_context_base_permissions_policy_unittest.cc
index 7d50bd7..b8b7ff0 100644
--- a/chrome/browser/permissions/permission_context_base_permissions_policy_unittest.cc
+++ b/chrome/browser/permissions/permission_context_base_permissions_policy_unittest.cc
@@ -64,7 +64,7 @@
     if (feature != blink::mojom::PermissionsPolicyFeature::kNotFound) {
       frame_policy.emplace_back(
           feature,
-          std::vector({blink::OriginWithPossibleWildcards::FromOrigin(
+          std::vector({*blink::OriginWithPossibleWildcards::FromOrigin(
               url::Origin::Create(GURL(origin)))}),
           /*self_if_matches=*/absl::nullopt,
           /*matches_all_origins=*/false,
@@ -91,7 +91,7 @@
     std::vector<blink::OriginWithPossibleWildcards> parsed_origins;
     for (const std::string& origin : origins) {
       parsed_origins.emplace_back(
-          blink::OriginWithPossibleWildcards::FromOrigin(
+          *blink::OriginWithPossibleWildcards::FromOrigin(
               url::Origin::Create(GURL(origin))));
     }
     navigation->SetPermissionsPolicyHeader(
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker_factory.cc b/chrome/browser/permissions/permission_decision_auto_blocker_factory.cc
index d34463c3..cea736a 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker_factory.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker_factory.cc
@@ -18,7 +18,8 @@
 // static
 PermissionDecisionAutoBlockerFactory*
 PermissionDecisionAutoBlockerFactory::GetInstance() {
-  return base::Singleton<PermissionDecisionAutoBlockerFactory>::get();
+  static base::NoDestructor<PermissionDecisionAutoBlockerFactory> instance;
+  return instance.get();
 }
 
 PermissionDecisionAutoBlockerFactory::PermissionDecisionAutoBlockerFactory()
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker_factory.h b/chrome/browser/permissions/permission_decision_auto_blocker_factory.h
index 2f0b4c4..7f280ec 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker_factory.h
+++ b/chrome/browser/permissions/permission_decision_auto_blocker_factory.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_PERMISSION_DECISION_AUTO_BLOCKER_FACTORY_H_
 #define CHROME_BROWSER_PERMISSIONS_PERMISSION_DECISION_AUTO_BLOCKER_FACTORY_H_
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
 class Profile;
@@ -26,8 +26,7 @@
       const PermissionDecisionAutoBlockerFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      PermissionDecisionAutoBlockerFactory>;
+  friend base::NoDestructor<PermissionDecisionAutoBlockerFactory>;
 
   PermissionDecisionAutoBlockerFactory();
   ~PermissionDecisionAutoBlockerFactory() override;
diff --git a/chrome/browser/permissions/permission_manager_factory.cc b/chrome/browser/permissions/permission_manager_factory.cc
index 0347a2d..bac1111 100644
--- a/chrome/browser/permissions/permission_manager_factory.cc
+++ b/chrome/browser/permissions/permission_manager_factory.cc
@@ -151,7 +151,8 @@
 
 // static
 PermissionManagerFactory* PermissionManagerFactory::GetInstance() {
-  return base::Singleton<PermissionManagerFactory>::get();
+  static base::NoDestructor<PermissionManagerFactory> instance;
+  return instance.get();
 }
 
 PermissionManagerFactory::PermissionManagerFactory()
@@ -166,7 +167,7 @@
   DependsOn(HostContentSettingsMapFactory::GetInstance());
 }
 
-PermissionManagerFactory::~PermissionManagerFactory() {}
+PermissionManagerFactory::~PermissionManagerFactory() = default;
 
 KeyedService* PermissionManagerFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
diff --git a/chrome/browser/permissions/permission_manager_factory.h b/chrome/browser/permissions/permission_manager_factory.h
index b37f678..472938a75 100644
--- a/chrome/browser/permissions/permission_manager_factory.h
+++ b/chrome/browser/permissions/permission_manager_factory.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_PERMISSIONS_PERMISSION_MANAGER_FACTORY_H_
 #define CHROME_BROWSER_PERMISSIONS_PERMISSION_MANAGER_FACTORY_H_
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
 namespace content {
@@ -27,7 +27,7 @@
   PermissionManagerFactory& operator=(const PermissionManagerFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<PermissionManagerFactory>;
+  friend base::NoDestructor<PermissionManagerFactory>;
 
   PermissionManagerFactory();
   ~PermissionManagerFactory() override;
diff --git a/chrome/browser/permissions/prediction_model_handler_provider_factory.cc b/chrome/browser/permissions/prediction_model_handler_provider_factory.cc
index 977b418..b6e614ac 100644
--- a/chrome/browser/permissions/prediction_model_handler_provider_factory.cc
+++ b/chrome/browser/permissions/prediction_model_handler_provider_factory.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/permissions/prediction_model_handler_provider_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -16,7 +16,8 @@
 // static
 PredictionModelHandlerProviderFactory*
 PredictionModelHandlerProviderFactory::GetInstance() {
-  return base::Singleton<PredictionModelHandlerProviderFactory>::get();
+  static base::NoDestructor<PredictionModelHandlerProviderFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/chrome/browser/permissions/prediction_model_handler_provider_factory.h b/chrome/browser/permissions/prediction_model_handler_provider_factory.h
index 09569d2..861f0cf 100644
--- a/chrome/browser/permissions/prediction_model_handler_provider_factory.h
+++ b/chrome/browser/permissions/prediction_model_handler_provider_factory.h
@@ -9,7 +9,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 namespace permissions {
 class PredictionModelHandlerProvider;
@@ -30,8 +30,7 @@
  private:
   PredictionModelHandlerProviderFactory();
   ~PredictionModelHandlerProviderFactory() override;
-  friend struct base::DefaultSingletonTraits<
-      PredictionModelHandlerProviderFactory>;
+  friend base::NoDestructor<PredictionModelHandlerProviderFactory>;
 
   // BrowserContextKeyedServiceFactory:
   KeyedService* BuildServiceInstanceFor(
diff --git a/chrome/browser/permissions/prediction_service_factory.cc b/chrome/browser/permissions/prediction_service_factory.cc
index 6eb6286d..bc347b4 100644
--- a/chrome/browser/permissions/prediction_service_factory.cc
+++ b/chrome/browser/permissions/prediction_service_factory.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/permissions/prediction_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/permissions/prediction_service/prediction_service.h"
@@ -19,7 +19,8 @@
 
 // static
 PredictionServiceFactory* PredictionServiceFactory::GetInstance() {
-  return base::Singleton<PredictionServiceFactory>::get();
+  static base::NoDestructor<PredictionServiceFactory> instance;
+  return instance.get();
 }
 
 PredictionServiceFactory::PredictionServiceFactory()
diff --git a/chrome/browser/permissions/prediction_service_factory.h b/chrome/browser/permissions/prediction_service_factory.h
index b9c93e43..d0d707b 100644
--- a/chrome/browser/permissions/prediction_service_factory.h
+++ b/chrome/browser/permissions/prediction_service_factory.h
@@ -11,7 +11,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 namespace permissions {
 class PredictionService;
@@ -26,7 +26,7 @@
   PredictionServiceFactory& operator=(const PredictionServiceFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<PredictionServiceFactory>;
+  friend base::NoDestructor<PredictionServiceFactory>;
 
   PredictionServiceFactory();
   ~PredictionServiceFactory() override;
diff --git a/chrome/browser/permissions/unused_site_permissions_service_factory.cc b/chrome/browser/permissions/unused_site_permissions_service_factory.cc
index 7a32a06..d039f993 100644
--- a/chrome/browser/permissions/unused_site_permissions_service_factory.cc
+++ b/chrome/browser/permissions/unused_site_permissions_service_factory.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/permissions/unused_site_permissions_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/permissions/unused_site_permissions_service.h"
@@ -12,7 +12,8 @@
 // static
 UnusedSitePermissionsServiceFactory*
 UnusedSitePermissionsServiceFactory::GetInstance() {
-  return base::Singleton<UnusedSitePermissionsServiceFactory>::get();
+  static base::NoDestructor<UnusedSitePermissionsServiceFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/chrome/browser/permissions/unused_site_permissions_service_factory.h b/chrome/browser/permissions/unused_site_permissions_service_factory.h
index a0975ca..839b5321 100644
--- a/chrome/browser/permissions/unused_site_permissions_service_factory.h
+++ b/chrome/browser/permissions/unused_site_permissions_service_factory.h
@@ -11,7 +11,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 
 namespace content {
@@ -36,8 +36,7 @@
       const UnusedSitePermissionsServiceFactory&) = delete;
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      UnusedSitePermissionsServiceFactory>;
+  friend base::NoDestructor<UnusedSitePermissionsServiceFactory>;
 
   UnusedSitePermissionsServiceFactory();
   ~UnusedSitePermissionsServiceFactory() override;
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.html b/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.html
index 371a8db9..964bbc2 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.html
@@ -66,11 +66,19 @@
     margin: 0;
   }
 
-  .sign {
+  .sign-button {
+    border-radius: 50%;
+    border-width: 0;
+    min-height: auto;
+    min-width: auto;
+    padding: 0;
+  }
+
+  .sign-icon {
     flex-grow: 0;
     flex-shrink: 0;
-    margin-inline-end: 13px;
-    margin-inline-start: 13px;
+    height: 16px;
+    padding: 13px;
     width: 16px;
   }
 
@@ -124,7 +132,7 @@
 
 <div id="displaySizeSelector">
   <!-- Preview Section -->
-  <div id="preview">
+  <div id="preview" aria-hidden="true">
     <p id="previewTitle">[[i18nDynamic(locale, 'displaySizePreview')]]</p>
     <div id="grid-container" clsas="layout horizontal center flex">
       <div id="grid">
@@ -146,11 +154,20 @@
     <slot name="slider-description"></slot>
   </div>
   <div id="sliderContainer">
-    <iron-icon id="negative" class="sign" icon="oobe-16:negative"></iron-icon>
+    <cr-button class="sign-button" on-click="onNegativeClicked_">
+      <iron-icon id="negative" class="sign-icon" icon="oobe-16:negative"
+          aria-label$="[[i18nDynamic(locale, 'displaySizeNegative')]]">
+      </iron-icon>
+    </cr-button>
     <cr-slider id="sizeSlider" value="[[tickedSizeIndex_]]"
         on-cr-slider-value-changed="onTickedSizeChanged_"
-        marker-count="[[markerCounts_]]" ticks="[[availableSizesTicks_]]">
+        marker-count="[[markerCounts_]]" ticks="[[availableSizesTicks_]]"
+        aria-labelledby="sliderTitleContainer" aria-live="polite">
     </cr-slider>
-    <iron-icon id="positive" class="sign" icon="oobe-16:positive"></iron-icon>
+    <cr-button class="sign-button" on-click="onPositiveClicked_">
+      <iron-icon id="positive" class="sign-icon" icon="oobe-16:positive"
+          aria-label$="[[i18nDynamic(locale, 'displaySizePositive')]]">
+      </iron-icon>
+    </cr-button>
   </div>
 </div>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.js b/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.js
index fd8ffc9..1a99617 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.js
+++ b/chrome/browser/resources/chromeos/login/components/oobe_display_size_selector.js
@@ -133,6 +133,20 @@
   onTickedSizeChanged_() {
     this.tickedSizeIndex_ = this.$.sizeSlider.value;
   }
+
+  onPositiveClicked_() {
+    if (this.$.sizeSlider.value + 1 < this.markerCounts_) {
+      this.$.sizeSlider.value += 1;
+      this.tickedSizeIndex_ += 1;
+    }
+  }
+
+  onNegativeClicked_() {
+    if (this.$.sizeSlider.value >= 1) {
+      this.$.sizeSlider.value -= 1;
+      this.tickedSizeIndex_ -= 1;
+    }
+  }
 }
 
 customElements.define(OobeDisplaySizeSelector.is, OobeDisplaySizeSelector);
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index 01160154..b5ba2d24 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -209,7 +209,7 @@
   deps = [
     "../../components:oobe_display_size_selector",
     "../../components/behaviors:login_screen_behavior",
-    "../../components/behaviors:oobe_dialog_host_behavior",
+    "../../components/behaviors:multi_step_behavior",
     "../../components/behaviors:oobe_i18n_behavior",
     "../../components/buttons:oobe_next_button",
     "../../components/buttons:oobe_text_button",
diff --git a/chrome/browser/resources/chromeos/login/screens/common/display_size.html b/chrome/browser/resources/chromeos/login/screens/common/display_size.html
index 8abac00..eb02f8db 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/display_size.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/display_size.html
@@ -6,7 +6,8 @@
 <style include="oobe-dialog-host-styles cros-color-overrides">
 </style>
 
-<oobe-adaptive-dialog id="displaySizeDialog">
+<oobe-adaptive-dialog id="displaySizeDialog" role="presentation"
+    for-step="overview">
   <iron-icon slot="icon" icon="oobe-32:display"></iron-icon>
   <h1 slot="title" id="display-size-title">
     [[i18nDynamic(locale, 'displaySizeTitle')]]
diff --git a/chrome/browser/resources/chromeos/login/screens/common/display_size.js b/chrome/browser/resources/chromeos/login/screens/common/display_size.js
index 00c4cf3..d4cff09 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/display_size.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/display_size.js
@@ -19,7 +19,7 @@
 import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.js';
-import {OobeDialogHostBehavior} from '../../components/behaviors/oobe_dialog_host_behavior.js';
+import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.js';
 import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../../components/behaviors/oobe_i18n_behavior.js';
 import {OOBE_UI_STATE} from '../../components/display_manager_types.js';
 
@@ -28,10 +28,21 @@
  * @extends {PolymerElement}
  * @implements {LoginScreenBehaviorInterface}
  * @implements {OobeI18nBehaviorInterface}
+ * @implements {MultiStepBehaviorInterface}
  */
 const DisplaySizeScreenElementBase = mixinBehaviors(
-    [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior],
-    PolymerElement);
+    [OobeI18nBehavior, LoginScreenBehavior, MultiStepBehavior], PolymerElement);
+
+/**
+ * Enum to represent steps on the display size screen.
+ * Currently there is only one step, but we still use
+ * MultiStepBehavior because it provides implementation of
+ * things like processing 'focus-on-show' class
+ * @enum {string}
+ */
+const DisplaySizeStep = {
+  OVERVIEW: 'overview',
+};
 
 /**
  * Available user actions.
@@ -67,6 +78,14 @@
     return [];
   }
 
+  get UI_STEPS() {
+    return DisplaySizeStep;
+  }
+
+  defaultUIStep() {
+    return DisplaySizeStep.OVERVIEW;
+  }
+
   /** @override */
   ready() {
     super.ready();
diff --git a/chrome/browser/resources/password_manager/password_manager_app.html b/chrome/browser/resources/password_manager/password_manager_app.html
index f7d45dbb..3d6b83e 100644
--- a/chrome/browser/resources/password_manager/password_manager_app.html
+++ b/chrome/browser/resources/password_manager/password_manager_app.html
@@ -23,6 +23,9 @@
     height: 100%;
     position: sticky;
     top: 0;
+    /* Ensure #sidebar stacking order is above page sections. */
+    /* This is necessary for password manager tutorial. */
+    z-index: 1;
   }
 
   #content {
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
index 4e7e0b0..1c8c011 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
@@ -93,7 +93,7 @@
 void BoundSessionCookieControllerImpl::OnCookieRefreshFetched(
     BoundSessionRefreshCookieFetcher::Result result) {
   refresh_cookie_fetcher_.reset();
-  if (result.net_error == net::OK && result.response_code == net::HTTP_OK) {
+  if (result == BoundSessionRefreshCookieFetcher::Result::kSuccess) {
     // Requests are resumed once the cookie is set in the cookie jar. The
     // cookie is expected to be fresh and `this` is notified with its
     // expiration date before `OnCookieRefreshFetched` is called.
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h
index db62988..f7a8b369 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h
@@ -14,10 +14,13 @@
 // created per request.
 class BoundSessionRefreshCookieFetcher {
  public:
-  struct Result {
-    net::Error net_error;
-    absl::optional<int> response_code;
+  enum class Result {
+    kSuccess = 0,
+    kConnectionError = 1,
+    kServerTransientError = 2,
+    kServerPersistentError = 3,
   };
+
   // Reports the result of the fetch request.
   using RefreshCookieCompleteCallback = base::OnceCallback<void(Result)>;
 
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
index 46be6f06..a6f038b 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
@@ -8,6 +8,7 @@
 
 #include "components/signin/public/base/signin_client.h"
 #include "google_apis/gaia/gaia_urls.h"
+#include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -102,7 +103,36 @@
     scoped_refptr<net::HttpResponseHeaders> headers) {
   net::Error net_error = static_cast<net::Error>(url_loader_->NetError());
 
-  std::move(callback_).Run(
-      Result(net_error, headers ? absl::optional<int>(headers->response_code())
-                                : absl::nullopt));
+  Result result = GetResultFromNetErrorAndHttpStatusCode(
+      net_error,
+      headers ? absl::optional<int>(headers->response_code()) : absl::nullopt);
+  std::move(callback_).Run(result);
+}
+
+BoundSessionRefreshCookieFetcher::Result
+BoundSessionRefreshCookieFetcherImpl::GetResultFromNetErrorAndHttpStatusCode(
+    net::Error net_error,
+    absl::optional<int> response_code) {
+  if ((net_error != net::OK &&
+       net_error != net::ERR_HTTP_RESPONSE_CODE_FAILURE) ||
+      !response_code) {
+    return BoundSessionRefreshCookieFetcher::Result::kConnectionError;
+  }
+
+  if (response_code == net::HTTP_OK) {
+    return BoundSessionRefreshCookieFetcher::Result::kSuccess;
+  }
+
+  if (response_code >= net::HTTP_INTERNAL_SERVER_ERROR) {
+    // Server error 5xx.
+    return BoundSessionRefreshCookieFetcher::Result::kServerTransientError;
+  }
+
+  if (response_code >= net::HTTP_BAD_REQUEST) {
+    // Server error 4xx.
+    return BoundSessionRefreshCookieFetcher::Result::kServerPersistentError;
+  }
+
+  // Unexpected response code.
+  return BoundSessionRefreshCookieFetcher::Result::kServerPersistentError;
 }
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
index 733ffba..52943f0 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
@@ -31,8 +31,14 @@
   void Start(RefreshCookieCompleteCallback callback) override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(BoundSessionRefreshCookieFetcherImplTest,
+                           GetResultFromNetErrorAndHttpStatusCode);
+
   void StartRefreshRequest();
   void OnURLLoaderComplete(scoped_refptr<net::HttpResponseHeaders> headers);
+  Result GetResultFromNetErrorAndHttpStatusCode(
+      net::Error net_error,
+      absl::optional<int> response_code);
 
   const raw_ptr<SigninClient> client_;
   const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
index d018592..36ae64f 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
@@ -19,8 +19,6 @@
 #include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace {
-
 class BoundSessionRefreshCookieFetcherImplTest : public ::testing::Test {
  public:
   BoundSessionRefreshCookieFetcherImplTest() {
@@ -55,8 +53,7 @@
       pending_request->request.url.spec(), "");
   EXPECT_TRUE(future.Wait());
   BoundSessionRefreshCookieFetcher::Result result = future.Get();
-  EXPECT_EQ(result.net_error, net::OK);
-  EXPECT_EQ(result.response_code, net::HTTP_OK);
+  EXPECT_EQ(result, BoundSessionRefreshCookieFetcher::Result::kSuccess);
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, FailureNetError) {
@@ -75,8 +72,7 @@
 
   EXPECT_TRUE(future.Wait());
   BoundSessionRefreshCookieFetcher::Result result = future.Get();
-  EXPECT_EQ(result.net_error, status.error_code);
-  EXPECT_FALSE(result.response_code.has_value());
+  EXPECT_EQ(result, BoundSessionRefreshCookieFetcher::Result::kConnectionError);
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, FailureHttpError) {
@@ -93,8 +89,38 @@
 
   EXPECT_TRUE(future.Wait());
   BoundSessionRefreshCookieFetcher::Result result = future.Get();
-  EXPECT_EQ(result.net_error, net::ERR_HTTP_RESPONSE_CODE_FAILURE);
-  EXPECT_EQ(result.response_code, net::HTTP_UNAUTHORIZED);
+  EXPECT_EQ(result,
+            BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+       GetResultFromNetErrorAndHttpStatusCode) {
+  // Connection error.
+  EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
+                net::ERR_CONNECTION_TIMED_OUT, absl::nullopt),
+            BoundSessionRefreshCookieFetcher::Result::kConnectionError);
+  // net::OK.
+  EXPECT_EQ(
+      fetcher_->GetResultFromNetErrorAndHttpStatusCode(net::OK, net::HTTP_OK),
+      BoundSessionRefreshCookieFetcher::Result::kSuccess);
+  // net::ERR_HTTP_RESPONSE_CODE_FAILURE
+  EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
+                net::ERR_HTTP_RESPONSE_CODE_FAILURE, net::HTTP_BAD_REQUEST),
+            BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+  // Persistent error.
+  EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
+                net::OK, net::HTTP_BAD_REQUEST),
+            BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+  EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
+                net::OK, net::HTTP_NOT_FOUND),
+            BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+  // Transient error.
+  EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
+                net::OK, net::HTTP_INTERNAL_SERVER_ERROR),
+            BoundSessionRefreshCookieFetcher::Result::kServerTransientError);
+  EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
+                net::OK, net::HTTP_GATEWAY_TIMEOUT),
+            BoundSessionRefreshCookieFetcher::Result::kServerTransientError);
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, NetworkDelayed) {
@@ -114,5 +140,3 @@
 
   EXPECT_TRUE(future.Wait());
 }
-
-}  // namespace
diff --git a/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc b/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc
index b4b1a07..fcfa930 100644
--- a/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc
+++ b/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc
@@ -51,7 +51,8 @@
     // Synchronous since tests use `BoundSessionTestCookieManager`.
     OnRefreshCookieCompleted(CreateFakeCookie(cookie_expiration.value()));
   } else {
-    std::move(callback_).Run(Result(net::Error::OK, net::HTTP_FORBIDDEN));
+    std::move(callback_).Run(
+        BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
   }
 }
 
@@ -83,9 +84,11 @@
     net::CookieAccessResult access_result) {
   bool success = access_result.status.IsInclude();
   if (!success) {
-    std::move(callback_).Run(Result(net::Error::OK, net::HTTP_FORBIDDEN));
+    std::move(callback_).Run(
+        BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
   } else {
-    std::move(callback_).Run(Result(net::Error::OK, net::HTTP_OK));
+    std::move(callback_).Run(
+        BoundSessionRefreshCookieFetcher::Result::kSuccess);
   }
   // |This| may be destroyed
 }
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml b/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml
index bfa75c40..6d740c38 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml
@@ -9,16 +9,60 @@
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center_horizontal"
-      android:layout_marginEnd="@dimen/touch_to_fill_sheet_margin_modern"
-      android:layout_marginStart="@dimen/touch_to_fill_sheet_margin_modern"
-      android:layout_marginTop="@dimen/ttf_drag_handlebar_margin"
-      android:layout_marginBottom="@dimen/ttf_drag_handlebar_margin"
+      android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
+      android:layout_marginVertical="@dimen/ttf_drag_handlebar_margin"
       android:importantForAccessibility="no"
       app:srcCompat="@drawable/drag_handlebar" />
   <ImageView
       android:id="@+id/touch_to_fill_sheet_header_image"
+      android:layout_width="@dimen/touch_to_fill_favicon_size_modern"
+      android:layout_height="@dimen/touch_to_fill_favicon_size_modern"
+      android:layout_gravity="center_horizontal"
+      android:layout_marginVertical="16dp"
+      android:importantForAccessibility="no" />
+  <TextView
+      android:id="@+id/touch_to_fill_sheet_title"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center_horizontal"
-      android:importantForAccessibility="no" />
+      android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
+      android:textAlignment="center"
+      android:textAppearance="@style/TextAppearance.Headline.Primary"
+      android:text="@string/password_generation_bottom_sheet_title"/>
+  <TextView
+      android:id="@+id/touch_to_fill_sheet_subtitle"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
+      android:layout_marginBottom="24dp"
+      android:textAlignment="center"
+      android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/>
+  <TextView
+      android:id="@+id/password"
+      android:layout_width="match_parent"
+      android:layout_height="52dp"
+      android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
+      android:layout_marginBottom="24dp"
+      android:background="@drawable/touch_to_fill_credential_background_modern_rounded_all"
+      android:singleLine="true"
+      android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+  <org.chromium.ui.widget.ButtonCompat
+      android:id="@+id/use_password_button"
+      android:text="@string/password_generation_bottom_sheet_use_password_button"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="14dp"
+      android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
+      android:gravity="center"
+      style="style/FilledButton.Flat"/>
+  <org.chromium.ui.widget.ButtonCompat
+      android:id="@+id/create_password_button"
+      android:text="@string/password_generation_bottom_sheet_dismiss_button"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="20dp"
+      android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
+      android:gravity="center"
+      style="@style/TextButton"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java
index 6033d7d..c5506192 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java
@@ -8,6 +8,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 import androidx.appcompat.content.res.AppCompatResources;
@@ -28,6 +29,11 @@
         ImageView sheetHeaderImage = mContent.findViewById(R.id.touch_to_fill_sheet_header_image);
         sheetHeaderImage.setImageDrawable(AppCompatResources.getDrawable(
                 context, PasswordManagerResourceProviderFactory.create().getPasswordManagerIcon()));
+        // TODO (crbug.com/1421753): Use real user account here instead of the fake one.
+        TextView sheetSubtitle = mContent.findViewById(R.id.touch_to_fill_sheet_subtitle);
+        sheetSubtitle.setText(
+                String.format(context.getString(R.string.password_generation_bottom_sheet_subtitle),
+                        "elisa.becket@gmail.com"));
     }
 
     @Override
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
index 3beb757..3a3183d4 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
@@ -26,8 +26,8 @@
 
 namespace {
 
-using ToShowVirtualKeyboard =
-    password_manager::PasswordManagerDriver::ToShowVirtualKeyboard;
+using ShowVirtualKeyboard =
+    password_manager::PasswordManagerDriver::ShowVirtualKeyboard;
 using autofill::mojom::SubmissionReadinessState;
 using password_manager::PasswordManagerDriver;
 using password_manager::UiCredential;
@@ -230,7 +230,7 @@
 
   password_manager::metrics_util::LogFilledCredentialIsFromAndroidApp(
       credential.is_affiliation_based_match().value());
-  driver_->KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false));
+  driver_->KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false));
 
   driver_->FillSuggestion(credential.username(), credential.password());
 
@@ -256,6 +256,6 @@
     bool show_virtual_keyboard) {
   std::exchange(driver_, nullptr)
       ->KeyboardReplacingSurfaceClosed(
-          ToShowVirtualKeyboard(show_virtual_keyboard));
+          ShowVirtualKeyboard(show_virtual_keyboard));
   base::UmaHistogramEnumeration("PasswordManager.TouchToFill.Outcome", outcome);
 }
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
index 417c83f..e8de5115 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
@@ -38,8 +38,8 @@
 
 namespace {
 
-using ToShowVirtualKeyboard =
-    password_manager::PasswordManagerDriver::ToShowVirtualKeyboard;
+using ShowVirtualKeyboard =
+    password_manager::PasswordManagerDriver::ShowVirtualKeyboard;
 using autofill::mojom::SubmissionReadinessState;
 using base::test::RunOnceCallback;
 using device_reauth::DeviceAuthRequester;
@@ -83,7 +83,7 @@
               (override));
   MOCK_METHOD(void,
               KeyboardReplacingSurfaceClosed,
-              (ToShowVirtualKeyboard),
+              (ShowVirtualKeyboard),
               (override));
   MOCK_METHOD(void, TriggerFormSubmission, (), (override));
   MOCK_METHOD(const GURL&, GetLastCommittedURL, (), (const override));
@@ -226,7 +226,7 @@
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
   touch_to_fill_controller().OnCredentialSelected(credentials[0]);
   histogram_tester().ExpectUniqueSample(
       "PasswordManager.TouchToFill.NumCredentialsShown", 1, 1);
@@ -269,7 +269,7 @@
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
   EXPECT_CALL(driver(), TriggerFormSubmission());
   EXPECT_CALL(client(), StartSubmissionTrackingAfterTouchToFill(Eq(u"alice")));
 
@@ -299,7 +299,7 @@
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
 
   EXPECT_CALL(driver(), TriggerFormSubmission()).Times(0);
   EXPECT_CALL(client(), StartSubmissionTrackingAfterTouchToFill(_)).Times(0);
@@ -336,7 +336,7 @@
   EXPECT_CALL(driver(),
               FillSuggestion(std::u16string(u""), std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
 
   touch_to_fill_controller().OnCredentialSelected(credentials[0]);
 }
@@ -368,7 +368,7 @@
   EXPECT_CALL(driver(),
               FillSuggestion(std::u16string(u""), std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
 
   touch_to_fill_controller().OnCredentialSelected(credentials[0]);
 }
@@ -392,7 +392,7 @@
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
 
   EXPECT_CALL(*authenticator(), CanAuthenticateWithBiometrics)
       .WillOnce(Return(false));
@@ -430,7 +430,7 @@
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
 
   EXPECT_CALL(*authenticator(), CanAuthenticateWithBiometrics)
       .WillOnce(Return(true));
@@ -462,7 +462,7 @@
 
   EXPECT_CALL(driver(), FillSuggestion(_, _)).Times(0);
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(true)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(true)));
 
   EXPECT_CALL(*authenticator(), CanAuthenticateWithBiometrics)
       .WillOnce(Return(true));
@@ -540,7 +540,7 @@
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"bob"),
                                        std::u16string(u"s3cr3t")));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
   EXPECT_CALL(*authenticator(), CanAuthenticateWithBiometrics)
       .WillOnce(Return(false));
   touch_to_fill_controller().OnCredentialSelected(credentials[1]);
@@ -612,7 +612,7 @@
           autofill::mojom::SubmissionReadinessState::kNoInformation));
 
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(true)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(true)));
   touch_to_fill_controller().OnDismiss();
 
   auto entries = test_recorder().GetEntriesByName(UkmBuilder::kEntryName);
@@ -648,7 +648,7 @@
           autofill::mojom::SubmissionReadinessState::kNoInformation));
 
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
   EXPECT_CALL(client(),
               NavigateToManagePasswordsPage(
                   password_manager::ManagePasswordsReferrer::kTouchToFill));
@@ -723,7 +723,7 @@
   EXPECT_CALL(*webauthn_credentials_delegate(),
               SelectPasskey(base::Base64Encode(credential.credential_id())));
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
   touch_to_fill_controller().OnPasskeyCredentialSelected(credentials[0]);
   histogram_tester().ExpectUniqueSample(
       "PasswordManager.TouchToFill.NumCredentialsShown", 1, 1);
@@ -762,7 +762,7 @@
       credentials, {}, MakeTouchToFillControllerDelegate(submission_readiness));
 
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(false)));
   EXPECT_CALL(driver(),
               FillSuggestion(credential.username(), credential.password()));
   EXPECT_CALL(driver(), TriggerFormSubmission())
@@ -790,7 +790,7 @@
       credentials, {}, MakeTouchToFillControllerDelegate(submission_readiness));
 
   EXPECT_CALL(driver(),
-              KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(true)));
+              KeyboardReplacingSurfaceClosed(ShowVirtualKeyboard(true)));
   touch_to_fill_controller().OnDismiss();
 
   uma_recorder.ExpectUniqueSample(
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 2ababf7..9a16b3f 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -5776,6 +5776,18 @@
       <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_CLOSED" desc="Accessibility string read when the password generation bottom sheet is closed.">
         Password suggestion is closed.
       </message>
+      <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_TITLE" desc="The title of the password generation bottom sheet.">
+        Use strong password?
+      </message>
+      <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE" desc="The subtitle for the password generation bottom sheet.">
+        You won’t need to remember this password. It will be saved to Google Password Manager for <ph name="USERNAME">%1$s<ex>elisa.becket@gmail.com</ex></ph>.
+      </message>
+      <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_USE_PASSWORD_BUTTON" desc="Appears on the button, which reflects the user agreement to user the generated strong password.">
+        Use password
+      </message>
+      <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_DISMISS_BUTTON" desc="Appears on the button, which reflects that the user has rejected the proposed generated password and decided to continue with creating their own password.">
+        Create my own
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_DISMISS_BUTTON.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_DISMISS_BUTTON.png.sha1
new file mode 100644
index 0000000..04b89c2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_DISMISS_BUTTON.png.sha1
@@ -0,0 +1 @@
+32e515c8e2b29c19264ccb73e553a3987a5b7404
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE.png.sha1
new file mode 100644
index 0000000..04b89c2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+32e515c8e2b29c19264ccb73e553a3987a5b7404
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_TITLE.png.sha1
new file mode 100644
index 0000000..04b89c2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_TITLE.png.sha1
@@ -0,0 +1 @@
+32e515c8e2b29c19264ccb73e553a3987a5b7404
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_USE_PASSWORD_BUTTON.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_USE_PASSWORD_BUTTON.png.sha1
new file mode 100644
index 0000000..04b89c2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_USE_PASSWORD_BUTTON.png.sha1
@@ -0,0 +1 @@
+32e515c8e2b29c19264ccb73e553a3987a5b7404
\ No newline at end of file
diff --git a/chrome/browser/ui/autofill/payments/save_upi_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_upi_bubble_controller_impl.cc
index fb1e6bf..90f00f9 100644
--- a/chrome/browser/ui/autofill/payments/save_upi_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/save_upi_bubble_controller_impl.cc
@@ -19,7 +19,11 @@
     : content::WebContentsUserData<SaveUPIBubbleControllerImpl>(*web_contents) {
 }
 
-SaveUPIBubbleControllerImpl::~SaveUPIBubbleControllerImpl() = default;
+SaveUPIBubbleControllerImpl::~SaveUPIBubbleControllerImpl() {
+  if (save_upi_bubble_) {
+    save_upi_bubble_->Hide();
+  }
+}
 
 void SaveUPIBubbleControllerImpl::OfferUpiIdLocalSave(
     const std::string& upi_id,
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index 64eade4..cb54736e 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -300,6 +300,7 @@
   E_CPONLY(kColorProfileMenuHeaderLabel) \
   E_CPONLY(kColorProfileMenuIconButton) \
   E_CPONLY(kColorProfileMenuIconButtonBackground) \
+  E_CPONLY(kColorProfileMenuIconButtonBackgroundHovered) \
   /* Profiles colors. */ \
   E_CPONLY(kColorProfilesReauthDialogBorder) \
   /* PWA colors. */ \
diff --git a/chrome/browser/ui/color/material_chrome_color_mixer.cc b/chrome/browser/ui/color/material_chrome_color_mixer.cc
index f6be1c3..d679001 100644
--- a/chrome/browser/ui/color/material_chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/material_chrome_color_mixer.cc
@@ -50,6 +50,8 @@
   mixer[kColorProfileMenuHeaderLabel] = {ui::kColorSysOnTonalContainer};
   mixer[kColorProfileMenuIconButton] = {ui::kColorSysOnTonalContainer};
   mixer[kColorProfileMenuIconButtonBackground] = {ui::kColorSysTonalContainer};
+  mixer[kColorProfileMenuIconButtonBackgroundHovered] = {
+      ui::kColorSysStateHoverOnSubtle};
 
   if (!ShouldApplyChromeMaterialOverrides(key)) {
     return;
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 7135cd7..86911d7d 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -47,6 +47,9 @@
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/animation/ink_drop_host.h"
+#include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/label_button.h"
@@ -58,6 +61,7 @@
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/layout_manager.h"
 #include "ui/views/layout/table_layout.h"
 #include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
@@ -222,9 +226,26 @@
     const int kBorderThickness = show_border_ ? 1 : 0;
     const SkScalar kButtonRadius = (button_size_ + 2 * kBorderThickness) / 2.0f;
     if (features::IsChromeRefresh2023() && has_background_color_) {
-      // TODO(crbug.com/1422119): Set colors for hover and pressed states.
       SetBackground(views::CreateThemedRoundedRectBackground(
           kColorProfileMenuIconButtonBackground, kButtonRadius));
+      views::InkDrop::Get(this)->GetInkDrop()->SetShowHighlightOnHover(true);
+      views::InkDrop::Get(this)->SetLayerRegion(views::LayerRegion::kAbove);
+      views::InkDrop::Get(this)->SetCreateHighlightCallback(base::BindRepeating(
+          [](CircularImageButton* host) {
+            const auto* color_provider = host->GetColorProvider();
+            const SkColor hover_color = color_provider->GetColor(
+                kColorProfileMenuIconButtonBackgroundHovered);
+            const float hover_alpha = SkColorGetA(hover_color);
+
+            auto ink_drop_highlight = std::make_unique<views::InkDropHighlight>(
+                host->size(), host->height() / 2,
+                gfx::PointF(host->GetLocalBounds().CenterPoint()),
+                SkColorSetA(hover_color, SK_AlphaOPAQUE));
+            ink_drop_highlight->set_visible_opacity(hover_alpha /
+                                                    SK_AlphaOPAQUE);
+            return ink_drop_highlight;
+          },
+          this));
     }
 
     // TODO(crbug.com/1422119): Remove border for Chrome Refresh 2023.
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
index 5a7143f..287c597 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
@@ -385,7 +385,14 @@
 }
 
 // Test that the extension's side panel entry shows the extension's icon.
-IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, EntryShowsExtensionIcon) {
+// TODO(crbug.com/1450850): Re-enable this test
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_EntryShowsExtensionIcon DISABLED_EntryShowsExtensionIcon
+#else
+#define MAYBE_EntryShowsExtensionIcon EntryShowsExtensionIcon
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
+                       MAYBE_EntryShowsExtensionIcon) {
   // Load an extension and verify that its SidePanelEntry is registered.
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
diff --git a/chrome/browser/ui/views/side_panel/search_companion/companion_page_browsertest.cc b/chrome/browser/ui/views/side_panel/search_companion/companion_page_browsertest.cc
index e728b79..eab9b1a8 100644
--- a/chrome/browser/ui/views/side_panel/search_companion/companion_page_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/search_companion/companion_page_browsertest.cc
@@ -584,7 +584,8 @@
   EXPECT_EQ(proto->page_url(), CreateUrl(kHost, kRelativeUrl1));
 }
 
-IN_PROC_BROWSER_TEST_F(CompanionPageBrowserTest, AutoRefreshOnTabForegrounded) {
+IN_PROC_BROWSER_TEST_F(CompanionPageBrowserTest,
+                       AutoRefreshOnSigninStateChange) {
   EnableSignInMsbbExps(/*signed_in=*/false, /*msbb=*/false, /*exps=*/false);
 
   // Load a page on the active tab and open companion side panel
@@ -595,6 +596,7 @@
   WaitForCompanionToBeLoaded();
   EXPECT_EQ(side_panel_coordinator()->GetCurrentEntryId(),
             SidePanelEntry::Id::kSearchCompanion);
+  auto* companion_web_contents = GetCompanionWebContents(browser());
 
   // Inspect the URL from the proto. This will reset the proto.
   auto proto = GetLastCompanionProtoFromUrlLoad();
@@ -604,44 +606,18 @@
   // Navigate to a new tab.
   chrome::NewTab(browser());
 
-  // Go back to the original tab. This should refresh the companion.
-  browser()->tab_strip_model()->ActivateTabAt(0);
-  WaitForCompanionIframeReload();
+  // Sign-in to chrome. The companion should refresh automatically even though
+  // it's in background.
+  content::TestNavigationObserver nav_observer(companion_web_contents, 1);
+  EnableSignInMsbbExps(/*signed_in=*/true, /*msbb=*/false, /*exps=*/false);
 
+  nav_observer.Wait();
   proto = GetLastCompanionProtoFromUrlLoad();
   EXPECT_TRUE(proto.has_value());
   EXPECT_TRUE(proto->page_url().empty());
 }
 
 IN_PROC_BROWSER_TEST_F(CompanionPageBrowserTest,
-                       DontAutoRefreshIfHasAllPermissions) {
-  EnableSignInMsbbExps(/*signed_in=*/true, /*msbb=*/true, /*exps=*/true);
-
-  // Load a page on the active tab and open companion side panel
-  ASSERT_TRUE(
-      ui_test_utils::NavigateToURL(browser(), CreateUrl(kHost, kRelativeUrl1)));
-  side_panel_coordinator()->Show(SidePanelEntry::Id::kSearchCompanion);
-
-  WaitForCompanionToBeLoaded();
-  EXPECT_EQ(side_panel_coordinator()->GetCurrentEntryId(),
-            SidePanelEntry::Id::kSearchCompanion);
-
-  // Inspect the URL from the proto. This will reset the proto.
-  auto proto = GetLastCompanionProtoFromUrlLoad();
-  EXPECT_TRUE(proto.has_value());
-  EXPECT_FALSE(proto->page_url().empty());
-  EXPECT_EQ(proto->page_url(), CreateUrl(kHost, kRelativeUrl1));
-
-  // Navigate to a new tab.
-  chrome::NewTab(browser());
-
-  // Go back to the original tab. This should not refresh the companion.
-  browser()->tab_strip_model()->ActivateTabAt(0);
-  proto = GetLastCompanionProtoFromUrlLoad();
-  EXPECT_FALSE(proto.has_value());
-}
-
-IN_PROC_BROWSER_TEST_F(CompanionPageBrowserTest,
                        SamePageNavigationsAreSkipped) {
   EnableSignInMsbbExps(/*signed_in=*/true, /*msbb=*/true, /*exps=*/true);
 
diff --git a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
index 037e7e7..980c404 100644
--- a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
+++ b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
@@ -44,6 +44,7 @@
 namespace {
 constexpr base::StringPiece kTestManifest = R"({
       "name": "Simple Isolated App",
+      "version": "$1",
       "id": "/",
       "scope": "/",
       "start_url": "/",
@@ -137,6 +138,7 @@
       web_package::SignedWebBundleId::CreateRandomForDevelopment());
   WebAppProvider::GetForWebApps(profile)->scheduler().InstallIsolatedWebApp(
       url_info, DevModeProxy{.proxy_url = proxy_origin},
+      /*expected_version=*/absl::nullopt,
       /*optional_keep_alive=*/nullptr,
       /*optional_profile_keep_alive=*/nullptr, future.GetCallback());
 
@@ -234,10 +236,12 @@
           key_pair_.public_key));
 }
 
-TestSignedWebBundle BuildDefaultTestSignedWebBundle() {
+TestSignedWebBundle BuildDefaultTestSignedWebBundle(
+    const base::Version& version) {
   TestSignedWebBundleBuilder builder = TestSignedWebBundleBuilder(
       web_package::WebBundleSigner::KeyPair(kTestPublicKey, kTestPrivateKey));
-  builder.AddManifest(kTestManifest);
+  builder.AddManifest(base::ReplaceStringPlaceholders(
+      kTestManifest, {version.GetString()}, nullptr));
   builder.AddPngImage(kTestIconUrl, GetTestIconInString());
   return builder.Build();
 }
@@ -253,8 +257,8 @@
   const AppId app_id = isolated_web_app->app_id();
   isolated_web_app->SetName(name);
   isolated_web_app->SetScope(isolated_web_app->start_url());
-  isolated_web_app->SetIsolationData(
-      WebApp::IsolationData(InstalledBundle{.path = base::FilePath()}));
+  isolated_web_app->SetIsolationData(WebApp::IsolationData(
+      InstalledBundle{.path = base::FilePath()}, base::Version("1.0.0")));
 
   ScopedRegistryUpdate update(&provider->sync_bridge_unsafe());
   update->CreateApp(std::move(isolated_web_app));
diff --git a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h
index 19f707e..5782d51 100644
--- a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h
+++ b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h
@@ -128,7 +128,8 @@
   web_package::WebBundleBuilder builder_;
 };
 
-TestSignedWebBundle BuildDefaultTestSignedWebBundle();
+TestSignedWebBundle BuildDefaultTestSignedWebBundle(
+    const base::Version& version = base::Version("1.0.0"));
 
 // Adds an Isolated Web App to the WebAppRegistrar. The IWA will have an empty
 // filepath for |IsolatedWebAppLocation|.
diff --git a/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc b/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc
index eab606e..716ca03 100644
--- a/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc
@@ -38,6 +38,10 @@
   builder->Add("displaySizeSettingsApp",
                IDS_OOBE_DISPLAY_SIZE_SETTINGS_APP_NAME);
   builder->Add("displaySizeValue", IDS_OOBE_DISPLAY_SIZE_VALUE);
+  builder->Add("displaySizePositive",
+               IDS_OOBE_DISPLAY_SIZE_POSITIVE_BUTTON_ARIA);
+  builder->Add("displaySizeNegative",
+               IDS_OOBE_DISPLAY_SIZE_NEGATIVE_BUTTON_ARIA);
 
   // CHOOBE resources
   builder->Add("choobeDisplaySizeTitle",
diff --git a/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc b/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc
index c9c505a..3820039 100644
--- a/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/companion/text_finder/text_highlighter_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/side_panel/companion/companion_side_panel_controller_utils.h"
@@ -53,11 +54,12 @@
       consent_helper_(unified_consent::UrlKeyedDataCollectionConsentHelper::
                           NewAnonymizedDataCollectionConsentHelper(
                               GetProfile()->GetPrefs())) {
-  consent_helper_->AddObserver(this);
+  identity_manager_observation_.Observe(
+      IdentityManagerFactory::GetForProfile(GetProfile()));
+  consent_helper_observation_.Observe(consent_helper_.get());
 }
 
 CompanionPageHandler::~CompanionPageHandler() {
-  consent_helper_->RemoveObserver(this);
   if (web_contents() && !web_contents()->IsBeingDestroyed()) {
     auto* tab_helper =
         companion::CompanionTabHelper::FromWebContents(web_contents());
@@ -65,6 +67,18 @@
   }
 }
 
+void CompanionPageHandler::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event_details) {
+  // We only care about the sign-in state changes. Sync state change is already
+  // captured through consent helper observer.
+  if (event_details.GetEventTypeFor(signin::ConsentLevel::kSignin) ==
+      signin::PrimaryAccountChangeEvent::Type::kNone) {
+    return;
+  }
+
+  NotifyURLChanged(/*is_full_reload=*/true);
+}
+
 void CompanionPageHandler::OnUrlKeyedDataCollectionConsentStateChanged(
     unified_consent::UrlKeyedDataCollectionConsentHelper* consent_helper) {
   NotifyURLChanged(/*is_full_reload=*/true);
@@ -110,16 +124,6 @@
   NotifyURLChanged(/*is_full_reload=*/false);
 }
 
-void CompanionPageHandler::OnVisibilityChanged(content::Visibility visibility) {
-  // Refresh companion whenever the tab is back to foreground state.
-  // Do this only when the user didn't have all the access permissions to begin
-  // with.
-  if (visibility == content::Visibility::VISIBLE &&
-      !MeetsAllAccessRequirements()) {
-    NotifyURLChanged(/*is_full_reload=*/true);
-  }
-}
-
 void CompanionPageHandler::ShowUI() {
   if (auto embedder = companion_untrusted_ui_->embedder()) {
     embedder->ShowUI();
@@ -181,14 +185,6 @@
   }
 }
 
-bool CompanionPageHandler::MeetsAllAccessRequirements() {
-  auto* pref_service = GetProfile()->GetPrefs();
-  bool is_exps_opted_in = pref_service->GetBoolean(kExpsOptInStatusGrantedPref);
-  bool is_msbb_enabled =
-      IsUserPermittedToSharePageInfoWithCompanion(pref_service);
-  return signin_delegate_->IsSignedIn() && is_msbb_enabled && is_exps_opted_in;
-}
-
 void CompanionPageHandler::OnImageQuery(
     side_panel::mojom::ImageQuery image_query) {
   GURL modified_upload_url = url_builder_->AppendCompanionParamsToURL(
diff --git a/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h b/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h
index 1d2b850..33d9d4e 100644
--- a/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h
@@ -7,10 +7,12 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/companion/core/constants.h"
 #include "chrome/browser/companion/core/mojom/companion.mojom.h"
 #include "chrome/browser/ui/side_panel/side_panel_enums.h"
 #include "components/lens/buildflags.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -32,6 +34,7 @@
 class CompanionPageHandler
     : public side_panel::mojom::CompanionPageHandler,
       public content::WebContentsObserver,
+      public signin::IdentityManager::Observer,
       public unified_consent::UrlKeyedDataCollectionConsentHelper::Observer {
  public:
   CompanionPageHandler(
@@ -65,7 +68,10 @@
   // content::WebContentsObserver overrides.
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void OnVisibilityChanged(content::Visibility visibility) override;
+
+  // IdentityManager::Observer overrides.
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
 
   // UrlKeyedDataCollectionConsentHelper::Observer overrides.
   void OnUrlKeyedDataCollectionConsentStateChanged(
@@ -95,10 +101,6 @@
   void DidFinishFindingCqTexts(
       const std::vector<std::pair<std::string, bool>>& text_found_vec);
 
-  // Helper method to determine whether the user has met all the access
-  // requirements, i.e. signed in, msbb enabled, and has exps access.
-  bool MeetsAllAccessRequirements();
-
   mojo::Receiver<side_panel::mojom::CompanionPageHandler> receiver_;
   mojo::Remote<side_panel::mojom::CompanionPage> page_;
   raw_ptr<CompanionSidePanelUntrustedUI> companion_untrusted_ui_ = nullptr;
@@ -114,6 +116,15 @@
   // The current URL of the main frame.
   GURL page_url_;
 
+  // Observers for sign-in and MSBB status.
+  base::ScopedObservation<signin::IdentityManager,
+                          signin::IdentityManager::Observer>
+      identity_manager_observation_{this};
+  base::ScopedObservation<
+      unified_consent::UrlKeyedDataCollectionConsentHelper,
+      unified_consent::UrlKeyedDataCollectionConsentHelper::Observer>
+      consent_helper_observation_{this};
+
   base::WeakPtrFactory<CompanionPageHandler> weak_ptr_factory_{this};
 };
 }  // namespace companion
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 0e0fe38a..e9ab3495 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -280,6 +280,8 @@
     sources += [
       "chromeos_web_app_experiments.cc",
       "chromeos_web_app_experiments.h",
+      "isolated_web_apps/isolated_web_app_downloader.cc",
+      "isolated_web_apps/isolated_web_app_downloader.h",
       "isolated_web_apps/policy/isolated_web_app_external_install_options.cc",
       "isolated_web_apps/policy/isolated_web_app_external_install_options.h",
       "isolated_web_apps/policy/isolated_web_app_policy_constants.cc",
@@ -711,6 +713,7 @@
 
   if (is_chromeos) {
     sources += [
+      "isolated_web_apps/isolated_web_app_downloader_unittest.cc",
       "isolated_web_apps/policy/isolated_web_app_external_install_options_unittest.cc",
       "isolated_web_apps/policy/isolated_web_app_policy_manager_unittest.cc",
       "isolated_web_apps/update_manifest/update_manifest_fetcher_unittest.cc",
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
index 2bafdbb..a5721b3 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h"
 
+#include <array>
 #include <memory>
 #include <sstream>
 #include <string>
@@ -32,6 +33,7 @@
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_response_reader_factory.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_trust_checker.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_validator.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.h"
 #include "chrome/browser/web_applications/isolated_web_apps/pending_install_info.h"
 #include "chrome/browser/web_applications/locks/app_lock.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
@@ -74,6 +76,7 @@
 InstallIsolatedWebAppCommand::InstallIsolatedWebAppCommand(
     const IsolatedWebAppUrlInfo& url_info,
     const IsolatedWebAppLocation& location,
+    const absl::optional<base::Version>& expected_version,
     std::unique_ptr<content::WebContents> web_contents,
     std::unique_ptr<WebAppUrlLoader> url_loader,
     std::unique_ptr<ScopedKeepAlive> optional_keep_alive,
@@ -88,6 +91,7 @@
           std::make_unique<AppLockDescription>(url_info.app_id())),
       url_info_(url_info),
       location_(location),
+      expected_version_(expected_version),
       response_reader_factory_(std::move(response_reader_factory)),
       web_contents_(std::move(web_contents)),
       url_loader_(std::move(url_loader)),
@@ -278,6 +282,34 @@
   WebAppInstallInfo info(manifest.id);
   UpdateWebAppInfoFromManifest(manifest, manifest_url, &info);
 
+  if (!manifest.version.has_value()) {
+    return base::unexpected(
+        "Manifest `version` is not present. manifest_url: " +
+        manifest_url.possibly_invalid_spec());
+  }
+  std::string version_string = base::UTF16ToUTF8(*manifest.version);
+
+  base::expected<std::array<uint32_t, 3>, IwaVersionParseError>
+      version_components = ParseIwaVersionIntoComponents(version_string);
+  if (!version_components.has_value()) {
+    return base::unexpected(base::StrCat(
+        {"Failed to parse `version` from the manifest: It must be in the form "
+         "`x.y.z`, where `x`, `y`, and `z` are numbers without leading zeros. "
+         "Detailed error: ",
+         IwaVersionParseErrorToString(version_components.error()),
+         " Got: ", version_string}));
+  }
+  base::Version version(
+      std::vector(version_components->begin(), version_components->end()));
+  info.isolated_web_app_version = version;
+
+  if (expected_version_.has_value() && *expected_version_ != version) {
+    return base::unexpected(
+        "Expected version (" + expected_version_->GetString() +
+        ") does not match the version provided in the manifest (" +
+        version.GetString() + ")");
+  }
+
   std::string encoded_id = manifest.id.path();
 
   if (encoded_id != "/") {
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h
index ecb3797c..1139144 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h
@@ -16,6 +16,7 @@
 #include "base/strings/string_piece_forward.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "base/version.h"
 #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "chrome/browser/web_applications/commands/web_app_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/error/unusable_swbn_file_error.h"
@@ -28,6 +29,7 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_logging.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
 
 class GURL;
@@ -71,14 +73,17 @@
   static std::unique_ptr<IsolatedWebAppResponseReaderFactory>
   CreateDefaultResponseReaderFactory(const PrefService& prefs);
 
-  // |url_info| holds the origin information of the app. It is randomly
+  // `url_info` holds the origin information of the app. It is randomly
   // generated for dev-proxy and the public key of signed bundle. It is
   // guarantee to be valid.
   //
-  // |location| holds information about the mode(dev-mod-proxy/signed-bundle)
+  // `location` holds information about the mode(dev-mod-proxy/signed-bundle)
   // and the source.
   //
-  // |callback| must be not null.
+  // `expected_version`, if set, specifies the expected version of the IWA to
+  // install. If the version in the manifest differs, install is aborted.
+  //
+  // `callback` must be not null.
   //
   // The `id` in the application's manifest must equal "/".
   //
@@ -88,6 +93,7 @@
   explicit InstallIsolatedWebAppCommand(
       const IsolatedWebAppUrlInfo& url_info,
       const IsolatedWebAppLocation& location,
+      const absl::optional<base::Version>& expected_version,
       std::unique_ptr<content::WebContents> web_contents,
       std::unique_ptr<WebAppUrlLoader> url_loader,
       std::unique_ptr<ScopedKeepAlive> optional_keep_alive,
@@ -160,6 +166,7 @@
 
   IsolatedWebAppUrlInfo url_info_;
   IsolatedWebAppLocation location_;
+  absl::optional<base::Version> expected_version_;
 
   std::unique_ptr<IsolatedWebAppResponseReaderFactory> response_reader_factory_;
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
index 1bd0717..640f17f 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
@@ -133,6 +133,7 @@
   manifest->start_url = application_url.Resolve("/testing-start-url.html");
   manifest->display = DisplayMode::kStandalone;
   manifest->short_name = u"test short manifest name";
+  manifest->version = u"1.0.0";
   return manifest;
 }
 
@@ -252,6 +253,7 @@
     std::unique_ptr<WebAppUrlLoader> url_loader;
     std::unique_ptr<content::WebContents> web_contents;
     absl::optional<IsolatedWebAppLocation> location;
+    absl::optional<base::Version> expected_version;
     raw_ptr<WebAppInstallFinalizer> install_finalizer = nullptr;
     base::expected<void, UnusableSwbnFileError> bundle_status = base::ok();
   };
@@ -284,10 +286,10 @@
       url_loader = std::move(test_url_loader);
     }
 
-    auto command = CreateCommand(parameters.url_info, std::move(web_contents),
-                                 parameters.location, std::move(url_loader),
-                                 test_future.GetCallback(),
-                                 std::move(parameters.bundle_status));
+    auto command = CreateCommand(
+        parameters.url_info, std::move(web_contents), parameters.location,
+        parameters.expected_version, std::move(url_loader),
+        test_future.GetCallback(), std::move(parameters.bundle_status));
 
     command->SetDataRetrieverForTesting(
         data_retriever != nullptr ? std::move(data_retriever)
@@ -301,6 +303,7 @@
       const IsolatedWebAppUrlInfo& url_info,
       std::unique_ptr<content::WebContents> web_contents,
       absl::optional<IsolatedWebAppLocation> location,
+      absl::optional<base::Version> expected_version,
       std::unique_ptr<WebAppUrlLoader> url_loader,
       base::OnceCallback<
           void(base::expected<InstallIsolatedWebAppCommandSuccess,
@@ -311,7 +314,7 @@
     }
 
     return std::make_unique<InstallIsolatedWebAppCommand>(
-        url_info, location.value(), std::move(web_contents),
+        url_info, location.value(), expected_version, std::move(web_contents),
         std::move(url_loader), /*optional_keep_alive=*/nullptr,
         /*optional_profile_keep_alive=*/nullptr, std::move(callback),
         std::make_unique<FakeResponseReaderFactory>(std::move(bundle_status)));
@@ -558,6 +561,64 @@
               IsInstallationError(HasSubstr("App is not installable")));
 }
 
+struct InvalidVersionParam {
+  absl::optional<std::u16string> version;
+  std::string error;
+  std::string test_name;
+};
+
+class InstallIsolatedWebAppCommandInvalidVersionTest
+    : public InstallIsolatedWebAppCommandTest,
+      public ::testing::WithParamInterface<InvalidVersionParam> {};
+
+TEST_P(InstallIsolatedWebAppCommandInvalidVersionTest, InstallationFails) {
+  IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo();
+  std::unique_ptr<MockDataRetriever> fake_data_retriever =
+      CreateDefaultDataRetriever(url_info.origin().GetURL());
+
+  auto manifest = CreateDefaultManifest(url_info.origin().GetURL());
+  manifest->version = GetParam().version;
+
+  ON_CALL(*fake_data_retriever, CheckInstallabilityAndRetrieveManifest)
+      .WillByDefault(ReturnManifest(
+          manifest, CreateDefaultManifestURL(url_info.origin().GetURL())));
+
+  EXPECT_THAT(ExecuteCommand(
+                  Parameters{
+                      .url_info = url_info,
+                  },
+                  std::move(fake_data_retriever)),
+              IsInstallationError(HasSubstr(GetParam().error)));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    InstallIsolatedWebAppCommandInvalidVersionTest,
+    ::testing::Values(InvalidVersionParam{.version = absl::nullopt,
+                                          .error = "`version` is not present",
+                                          .test_name = "NoVersion"},
+                      InvalidVersionParam{.version = u"10",
+                                          .error = "Failed to parse `version`",
+                                          .test_name = "InvalidVersionFormat"}),
+    [](const ::testing::TestParamInfo<
+        InstallIsolatedWebAppCommandInvalidVersionTest::ParamType>& info) {
+      return info.param.test_name;
+    });
+
+TEST_F(InstallIsolatedWebAppCommandTest,
+       InstallationFailsWhenAppVersionDoesNotMatchExpectedVersion) {
+  IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo();
+  std::unique_ptr<MockDataRetriever> fake_data_retriever =
+      CreateDefaultDataRetriever(url_info.origin().GetURL());
+
+  EXPECT_THAT(
+      ExecuteCommand(Parameters{.url_info = url_info,
+                                .expected_version = base::Version("99.99.99")},
+                     std::move(fake_data_retriever)),
+      IsInstallationError(
+          HasSubstr("does not match the version provided in the manifest")));
+}
+
 TEST_F(InstallIsolatedWebAppCommandTest, CommandLocksOnAppIdAndWebContents) {
   base::test::TestFuture<base::expected<InstallIsolatedWebAppCommandSuccess,
                                         InstallIsolatedWebAppCommandError>>
@@ -568,8 +629,9 @@
       url_info,
       content::WebContents::Create(
           content::WebContents::CreateParams(profile())),
-      CreateDevProxyLocation(), std::make_unique<TestWebAppUrlLoader>(),
-      test_future.GetCallback());
+      CreateDevProxyLocation(),
+      /*expected_version=*/absl::nullopt,
+      std::make_unique<TestWebAppUrlLoader>(), test_future.GetCallback());
   EXPECT_THAT(
       command->lock_description(),
       AllOf(Property(&LockDescription::type, Eq(LockDescription::Type::kApp)),
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc
index 0f99d7efc..e281693f 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc
@@ -252,7 +252,8 @@
   }
 
   command_scheduler_->InstallIsolatedWebApp(
-      url_info.value(), location, std::move(keep_alive),
+      url_info.value(), location,
+      /*expected_version=*/absl::nullopt, std::move(keep_alive),
       std::move(optional_profile_keep_alive),
       base::BindOnce(
           &IsolatedWebAppCommandLineInstallManager::OnInstallIsolatedWebApp,
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line_unittest.cc
index 6d700aba..4345b6a6b 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line_unittest.cc
@@ -49,8 +49,8 @@
                               MaybeIwaLocation arg) {
   if (arg.has_value()) {
     if (arg.value().has_value()) {
-      *result_listener
-          << WebApp::IsolationData(arg.value().value()).AsDebugValue();
+      *result_listener << IsolatedWebAppLocationAsDebugValue(
+          arg.value().value());
     } else {
       *result_listener << "nullopt";
     }
@@ -118,6 +118,7 @@
   void InstallIsolatedWebApp(
       const IsolatedWebAppUrlInfo& url_info,
       const IsolatedWebAppLocation& location,
+      const absl::optional<base::Version>& expected_version,
       std::unique_ptr<ScopedKeepAlive> keep_alive,
       std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive,
       WebAppCommandScheduler::InstallIsolatedWebAppCallback callback,
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.cc
new file mode 100644
index 0000000..fc018b1b
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.cc
@@ -0,0 +1,109 @@
+// 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/web_applications/isolated_web_apps/isolated_web_app_downloader.h"
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+namespace web_app {
+
+// static
+std::unique_ptr<IsolatedWebAppDownloader>
+IsolatedWebAppDownloader::CreateAndStartDownloading(
+    GURL url,
+    base::FilePath destination,
+    net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    IsolatedWebAppDownloader::DownloadCallback download_callback) {
+  auto downloader = base::WrapUnique(
+      new IsolatedWebAppDownloader(std::move(url_loader_factory)));
+  downloader->DownloadSignedWebBundle(std::move(url), std::move(destination),
+                                      std::move(partial_traffic_annotation),
+                                      std::move(download_callback));
+  return downloader;
+}
+
+IsolatedWebAppDownloader::IsolatedWebAppDownloader(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(std::move(url_loader_factory)) {}
+
+IsolatedWebAppDownloader::~IsolatedWebAppDownloader() = default;
+
+void IsolatedWebAppDownloader::DownloadSignedWebBundle(
+    GURL url,
+    base::FilePath destination,
+    net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation,
+    DownloadCallback download_callback) {
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::CompleteNetworkTrafficAnnotation("iwa_bundle_downloader",
+                                            partial_traffic_annotation,
+                                            R"(
+    semantics {
+      data:
+        "This request does not send any user data. Its destination is the URL "
+        "of a Signed Web Bundle of an Isolated Web App that is installed for "
+        "the user."
+      destination: OTHER
+      internal {
+        contacts {
+          email: "cmfcmf@google.com"
+        }
+      }
+      user_data {
+        type: NONE
+      }
+      last_reviewed: "2023-06-01"
+    }
+    policy {
+      cookies_allowed: NO
+    })");
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  // Cookies are not allowed.
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), std::move(traffic_annotation));
+
+  simple_url_loader_->SetRetryOptions(
+      /* max_retries=*/3,
+      network::SimpleURLLoader::RETRY_ON_5XX |
+          network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+
+  simple_url_loader_->DownloadToFile(
+      url_loader_factory_.get(),
+      base::BindOnce(&IsolatedWebAppDownloader::OnSignedWebBundleDownloaded,
+                     // The callback will never run if `this` is deleted,
+                     // because `simple_url_loader_` is a member of `this`.
+                     base::Unretained(this), destination)
+          .Then(std::move(download_callback)),
+      destination);
+}
+
+int32_t IsolatedWebAppDownloader::OnSignedWebBundleDownloaded(
+    base::FilePath destination,
+    base::FilePath actual_destination) {
+  if (actual_destination.empty()) {
+    int32_t net_error = simple_url_loader_->NetError();
+    CHECK_NE(net_error, net::OK);
+    return net_error;
+  }
+  CHECK_EQ(actual_destination, destination);
+  return net::OK;
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.h b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.h
new file mode 100644
index 0000000..0241b44
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.h
@@ -0,0 +1,58 @@
+// 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_WEB_APPLICATIONS_ISOLATED_WEB_APPS_ISOLATED_WEB_APP_DOWNLOADER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_ISOLATED_WEB_APP_DOWNLOADER_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace web_app {
+
+// Helper class to download the Signed Web Bundle of an Isolated Web App.
+class IsolatedWebAppDownloader {
+ public:
+  using DownloadCallback = base::OnceCallback<void(int32_t net_error)>;
+
+  // Creates a new instance of this class and starts the download process.
+  static std::unique_ptr<IsolatedWebAppDownloader> CreateAndStartDownloading(
+      GURL url,
+      base::FilePath destination,
+      net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      DownloadCallback download_callback);
+
+  ~IsolatedWebAppDownloader();
+
+ private:
+  explicit IsolatedWebAppDownloader(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+
+  void DownloadSignedWebBundle(
+      GURL url,
+      base::FilePath destination,
+      net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation,
+      DownloadCallback download_callback);
+
+  int32_t OnSignedWebBundleDownloaded(base::FilePath destination,
+                                      base::FilePath actual_destination);
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_ISOLATED_WEB_APP_DOWNLOADER_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader_unittest.cc
new file mode 100644
index 0000000..b98babdc
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader_unittest.cc
@@ -0,0 +1,84 @@
+// 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/web_applications/isolated_web_apps/isolated_web_app_downloader.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_app {
+namespace {
+
+using ::testing::Eq;
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+
+class IsolatedWebAppDownloaderTest : public ::testing::Test {
+ public:
+  IsolatedWebAppDownloaderTest()
+      : shared_url_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_factory_)) {}
+
+  void SetUp() override { CHECK(temp_dir_.CreateUniqueTempDir()); }
+
+  base::FilePath bundle_path() {
+    return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("bundle.swbn"));
+  }
+
+  GURL download_url() { return GURL("https://example.com/bundle.swbn"); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  network::TestURLLoaderFactory test_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
+
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(IsolatedWebAppDownloaderTest, SuccessfulDownload) {
+  test_factory_.AddResponse(download_url().spec(), "test bundle content",
+                            net::HttpStatusCode::HTTP_OK);
+
+  base::test::TestFuture<int32_t> future;
+  auto downloader = IsolatedWebAppDownloader::CreateAndStartDownloading(
+      download_url(), bundle_path(), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
+      shared_url_loader_factory_, future.GetCallback());
+
+  EXPECT_THAT(future.Take(), Eq(net::OK));
+  EXPECT_THAT(base::PathExists(bundle_path()), IsTrue());
+
+  std::string file_contents;
+  EXPECT_THAT(base::ReadFileToString(bundle_path(), &file_contents), IsTrue());
+  EXPECT_THAT(file_contents, Eq("test bundle content"));
+}
+
+TEST_F(IsolatedWebAppDownloaderTest, FailedDownload) {
+  test_factory_.AddResponse(download_url().spec(), "",
+                            net::HttpStatusCode::HTTP_NOT_FOUND);
+
+  base::test::TestFuture<int32_t> future;
+  auto downloader = IsolatedWebAppDownloader::CreateAndStartDownloading(
+      download_url(), bundle_path(), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
+      shared_url_loader_factory_, future.GetCallback());
+
+  EXPECT_THAT(future.Take(), Eq(net::Error::ERR_HTTP_RESPONSE_CODE_FAILURE));
+  EXPECT_THAT(base::PathExists(bundle_path()), IsFalse());
+}
+
+}  // namespace
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc
index 7ac0d18e..8ac53bf 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc
@@ -230,7 +230,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
@@ -249,7 +250,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
@@ -277,7 +279,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
@@ -293,7 +296,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
@@ -312,7 +316,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
@@ -397,8 +402,9 @@
 )js");
   RegisterWebApp(CreateIsolatedWebApp(
       GURL(kUrl),
-      WebApp::IsolationData{InstalledBundle{
-          .path = SignAndWriteBundleToDisk(builder.CreateBundle())}}));
+      WebApp::IsolationData{InstalledBundle{.path = SignAndWriteBundleToDisk(
+                                                builder.CreateBundle())},
+                            base::Version("1.0.0")}));
   TrustWebBundleId();
 
   NavigateAndWaitForTitle(GURL(kUrl),
@@ -412,7 +418,8 @@
     base::FilePath bundle_path =
         SignAndWriteBundleToDisk(builder.CreateBundle());
     std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-        kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+        kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                    base::Version("1.0.0")});
     RegisterWebApp(std::move(iwa));
     TrustWebBundleId();
 
@@ -518,7 +525,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
@@ -545,7 +553,8 @@
   base::FilePath bundle_path = SignAndWriteBundleToDisk(builder.CreateBundle());
 
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
-      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+      kUrl, WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                  base::Version("1.0.0")});
   RegisterWebApp(std::move(iwa));
   TrustWebBundleId();
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc
index da46d73..42b2167 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc
@@ -300,7 +300,8 @@
        RequestFailsWithErrFailedIfAppNotLocallyInstalled) {
   std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin}});
+      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                            base::Version("1.0.0")});
   iwa->SetIsLocallyInstalled(false);
   RegisterWebApp(std::move(iwa));
 
@@ -321,7 +322,8 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, GetRequestsSucceed) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -336,7 +338,8 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, HeadRequestsSucceed) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -352,7 +355,8 @@
        PostRequestsReturnMethodNotSupportedWhenAppIsInstalled) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -371,9 +375,11 @@
     IsolatedWebAppURLLoaderFactoryTest,
     PostRequestsReturnMethodNotSupportedWhenAppIsInstalledAndThereIsPendingInstall) {
   RegisterWebApp(CreateIsolatedWebApp(
-      kDevAppStartUrl, WebApp::IsolationData{DevModeProxy{
-                           .proxy_url = url::Origin::Create(
-                               GURL("http://installed-app-proxy-url.com"))}}));
+      kDevAppStartUrl,
+      WebApp::IsolationData{
+          DevModeProxy{.proxy_url = url::Origin::Create(
+                           GURL("http://installed-app-proxy-url.com"))},
+          base::Version("1.0.0")}));
 
   IsolatedWebAppPendingInstallInfo::FromWebContents(*web_contents())
       .set_isolated_web_app_location(
@@ -412,8 +418,10 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest,
        RequestFailsWithErrFailedIfStoragePartitionDoesNotExist) {
   RegisterWebApp(
-      CreateIsolatedWebApp(kDevAppStartUrl, WebApp::IsolationData{DevModeProxy{
-                                                .proxy_url = kProxyOrigin}}),
+      CreateIsolatedWebApp(
+          kDevAppStartUrl,
+          WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                                base::Version("1.0.0")}),
       /*create_storage_partition=*/false);
 
   CreateFactory();
@@ -429,7 +437,8 @@
        RequestUsesNonDefaultStoragePartition) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -444,8 +453,9 @@
        RequestSucceedsIfProxyUrlHasTrailingSlash) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com/"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com/"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -460,8 +470,9 @@
        RequestSucceedsIfProxyUrlDoesNotHaveTrailingSlash) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -475,8 +486,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyUrlDoesNotHaveUrlQuery) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -491,8 +503,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyUrlDoesNotHaveUrlFragment) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -507,8 +520,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyUrlKeepsOriginUrlPath) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -523,8 +537,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyUrlRemovesOriginalRequestData) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -542,8 +557,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyRequestCopiesAcceptHeader) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -562,8 +578,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyRequestDisablesCaching) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -582,8 +599,9 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyRequestDefaultsToAcceptingAll) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -602,8 +620,9 @@
        DoNotReturnGeneratedPageWhenNotInstallingApplication) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -627,8 +646,9 @@
 
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -653,8 +673,9 @@
 
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{
-          .proxy_url = url::Origin::Create(GURL("http://example.com"))}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = url::Origin::Create(
+                                             GURL("http://example.com"))},
+                            base::Version("1.0.0")}));
 
   CreateFactory();
 
@@ -714,7 +735,8 @@
 TEST_F(IsolatedWebAppURLLoaderFactoryForServiceWorkerTest, GetRequestsSucceed) {
   RegisterWebApp(CreateIsolatedWebApp(
       kDevAppStartUrl,
-      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin}}));
+      WebApp::IsolationData{DevModeProxy{.proxy_url = kProxyOrigin},
+                            base::Version("1.0.0")}));
 
   CreateFactoryForServiceWorker();
 
@@ -750,8 +772,10 @@
     std::unique_ptr<WebApp> iwa = CreateIsolatedWebApp(
         kEd25519AppOriginUrl,
         is_dev_mode_bundle_
-            ? WebApp::IsolationData{DevModeBundle{.path = bundle_path}}
-            : WebApp::IsolationData{InstalledBundle{.path = bundle_path}});
+            ? WebApp::IsolationData{DevModeBundle{.path = bundle_path},
+                                    base::Version("1.0.0")}
+            : WebApp::IsolationData{InstalledBundle{.path = bundle_path},
+                                    base::Version("1.0.0")});
     RegisterWebApp(std::move(iwa));
   }
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.cc
index cbb511f..34845c2 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.cc
@@ -59,4 +59,20 @@
   return components;
 }
 
+std::string IwaVersionParseErrorToString(IwaVersionParseError error) {
+  switch (error) {
+    case IwaVersionParseError::kNotThreeComponents:
+      return "A version must consist of exactly three components separated by "
+             "dots (`x.y.z`)";
+    case IwaVersionParseError::kEmptyComponent:
+      return "A version component may not be empty";
+    case IwaVersionParseError::kNonDigit:
+      return "A version component may only contain digits";
+    case IwaVersionParseError::kLeadingZero:
+      return "A version component may not have leading zeros";
+    case IwaVersionParseError::kCannotConvertToNumber:
+      return "A version component could not be converted into a number";
+  }
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.h b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.h
index 3836580..aac9f63dc 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.h
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.h
@@ -25,6 +25,8 @@
 base::expected<std::array<uint32_t, 3>, IwaVersionParseError>
 ParseIwaVersionIntoComponents(base::StringPiece version_string);
 
+std::string IwaVersionParseErrorToString(IwaVersionParseError error);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_ISOLATED_WEB_APP_VERSION_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version_unittest.cc
index efe6ef2..a4d7d57bf 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version_unittest.cc
@@ -17,6 +17,7 @@
 namespace {
 
 using testing::Eq;
+using testing::HasSubstr;
 using testing::IsTrue;
 
 struct IwaVersionTestParam {
@@ -105,5 +106,27 @@
          .expected_components =
              base::unexpected(IwaVersionParseError::kLeadingZero)}}));
 
+using IwaVersionParseErrorToStringTest =
+    ::testing::TestWithParam<std::pair<IwaVersionParseError, std::string>>;
+
+TEST_P(IwaVersionParseErrorToStringTest, ConvertsErrorToString) {
+  EXPECT_THAT(IwaVersionParseErrorToString(GetParam().first),
+              HasSubstr(GetParam().second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    IwaVersionParseErrorToStringTest,
+    ::testing::Values(
+        std::make_pair(IwaVersionParseError::kNotThreeComponents,
+                       "exactly three components"),
+        std::make_pair(IwaVersionParseError::kEmptyComponent,
+                       "may not be empty"),
+        std::make_pair(IwaVersionParseError::kNonDigit, "only contain digits"),
+        std::make_pair(IwaVersionParseError::kLeadingZero,
+                       "not have leading zeros"),
+        std::make_pair(IwaVersionParseError::kCannotConvertToNumber,
+                       "could not be converted into a number")));
+
 }  // namespace
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_external_install_options.h b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_external_install_options.h
index fc31b2a..b5c76b2 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_external_install_options.h
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_external_install_options.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_POLICY_ISOLATED_WEB_APP_EXTERNAL_INSTALL_OPTIONS_H_
 
 #include "base/files/file_path.h"
+#include "base/version.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
 #include "url/gurl.h"
 
@@ -37,10 +38,17 @@
     return web_bundle_id_;
   }
 
-  void set_web_bundle_url(const GURL& url) { web_bundle_url_ = url; }
+  void set_web_bundle_url_and_expected_version(
+      const GURL& url,
+      const base::Version& expected_version) {
+    web_bundle_url_ = url;
+    expected_version_ = expected_version;
+  }
 
   const GURL& web_bundle_url() const { return web_bundle_url_; }
 
+  const base::Version& expected_version() const { return expected_version_; }
+
   void set_app_directory(const base::FilePath& app_directory) {
     app_directory_ = app_directory;
   }
@@ -61,6 +69,8 @@
 
   // The URL to be used to download Web Bundle.
   GURL web_bundle_url_;
+  // The expected version of the downloaded Web Bundle.
+  base::Version expected_version_;
   // The directory where the Signed Web Bundle was or will be downloaded to.
   base::FilePath app_directory_;
 };
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
index 138faa38..0f0be5c 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
@@ -15,17 +15,19 @@
 #include "base/logging.h"
 #include "base/task/thread_pool.h"
 #include "base/types/expected.h"
+#include "base/version.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_constants.h"
 #include "chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.h"
 #include "chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
+#include "net/base/net_errors.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
@@ -50,13 +52,14 @@
 void IsolatedWebAppPolicyManager::IwaInstallCommandWrapperImpl::Install(
     const IsolatedWebAppLocation& location,
     const IsolatedWebAppUrlInfo& url_info,
+    const base::Version& expected_version,
     WebAppCommandScheduler::InstallIsolatedWebAppCallback callback) {
   // There is no need to keep the browser or profile alive when
   // policy-installing an IWA. If the browser or profile shut down, installation
   // will be re-attempted the next time they start, assuming that the policy is
   // still set.
   provider_->scheduler().InstallIsolatedWebApp(
-      url_info, location,
+      url_info, location, expected_version,
       /*optional_keep_alive=*/nullptr,
       /*optional_profile_keep_alive=*/nullptr, std::move(callback));
 }
@@ -220,7 +223,8 @@
   UpdateManifest::VersionEntry latest_version =
       GetLatestVersionEntry(*update_manifest);
 
-  current_app_->set_web_bundle_url(latest_version.src());
+  current_app_->set_web_bundle_url_and_expected_version(
+      latest_version.src(), latest_version.version());
   CreateIwaDirectory();
 }
 
@@ -248,67 +252,62 @@
 }
 
 void IsolatedWebAppPolicyManager::DownloadWebBundle() {
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("iwa_policy_signed_web_bundle", R"(
-  semantics {
-    sender: "Isolated Web App Signed Web Bundle Downloader"
-    description:
-      "Downloads the Signed Web Bundle of the Isolated Web App (IWA) "
-      "by the URL taken form the Update Manifest."
-    trigger:
-      "Installing/update of every IWA (including policy-based installs) "
-      "require in a Signed Web Bundle that we download here."
-    data:
-      "This request does not send any data. It just downloads a Web Bundle."
-    destination: OTHER
-    internal {
-      contacts {
-        email: "peletskyi@google.com"
+  net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+      net::DefinePartialNetworkTrafficAnnotation("iwa_policy_signed_web_bundle",
+                                                 "iwa_bundle_downloader",
+                                                 R"(
+    semantics {
+      sender: "Isolated Web App Policy Manager"
+      description:
+        "Downloads the Signed Web Bundle of an Isolated Web App (IWA) from the "
+        "URL read from an Update Manifest that is provided in an enterprise "
+        "policy by the administrator. The Signed Web Bundle contains code and "
+        "other resources of the IWA."
+      trigger:
+        "An Isolated Web App is installed from an enterprise policy."
+      # TODO(cmfcmf): `internal` and `user_data` is duplicated in
+      # `IsolatedWebAppDownloader::DownloadSignedWebBundle`, but the
+      # traffic annotator script complains that it is missing if it is not also
+      # present here.
+      internal {
+        contacts {
+          email: "cmfcmf@google.com"
+        }
       }
-    }
-    user_data {
-      type: NONE
-    }
-    last_reviewed: "2023-01-09"
-  }
-  policy {
-    cookies_allowed: NO
-    setting: "This feature cannot be disabled in settings."
-    chrome_policy {
-      IsolatedWebAppInstallForceList {
-        IsolatedWebAppInstallForceList: ""
+      user_data {
+        type: NONE
       }
+      last_reviewed: "2023-06-01"
     }
-  })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = current_app_->web_bundle_url();
-  // Cookies are not allowed.
-  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-
-  std::unique_ptr<network::SimpleURLLoader> simple_loader =
-      network::SimpleURLLoader::Create(std::move(resource_request),
-                                       std::move(traffic_annotation));
-
-  simple_loader->SetRetryOptions(
-      /* max_retries=*/3,
-      network::SimpleURLLoader::RETRY_ON_5XX |
-          network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
-
-  network::SimpleURLLoader* const simple_loader_ptr = simple_loader.get();
-  base::OnceCallback<void(base::FilePath path)> cb =
-      base::BindOnce(&IsolatedWebAppPolicyManager::OnWebBundleDownloaded,
-                     weak_factory_.GetWeakPtr(), std::move(simple_loader));
+    policy {
+      setting: "This feature cannot be disabled in settings."
+      chrome_policy {
+        IsolatedWebAppInstallForceList {
+          IsolatedWebAppInstallForceList: ""
+        }
+      }
+    })");
 
   base::FilePath swbn_path =
       current_app_->app_directory().Append(kMainSignedWebBundleFileName);
-  simple_loader_ptr->DownloadToFile(url_loader_factory_.get(), std::move(cb),
-                                    swbn_path);
+
+  current_bundle_downloader_ =
+      IsolatedWebAppDownloader::CreateAndStartDownloading(
+          current_app_->web_bundle_url(), swbn_path,
+          std::move(partial_traffic_annotation), url_loader_factory_,
+          base::BindOnce(
+              &IsolatedWebAppPolicyManager::OnWebBundleDownloaded,
+              // If `this` is deleted, `current_bundle_downloader_` is deleted
+              // as well, and thus the callback will never run.
+              base::Unretained(this), swbn_path));
 }
 
 void IsolatedWebAppPolicyManager::OnWebBundleDownloaded(
-    std::unique_ptr<network::SimpleURLLoader> simple_loader,
-    base::FilePath path) {
-  if (path.empty()) {
+    const base::FilePath& path,
+    int32_t net_error) {
+  current_bundle_downloader_.reset();
+
+  if (net_error != net::OK) {
     SetResultAndContinue(
         EphemeralAppInstallResult::kErrorCantDownloadWebBundle);
     return;
@@ -320,7 +319,7 @@
           current_app_->web_bundle_id());
 
   installer_->Install(
-      location, url_info,
+      location, url_info, current_app_->expected_version(),
       base::BindOnce(&IsolatedWebAppPolicyManager::OnIwaInstalled,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h
index 5bd2cfe2..cd20e657 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h
@@ -12,6 +12,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
+#include "base/version.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_location.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_external_install_options.h"
@@ -22,12 +23,12 @@
 
 namespace network {
 class SharedURLLoaderFactory;
-class SimpleURLLoader;
 }  // namespace network
 
 namespace web_app {
 
 class UpdateManifest;
+class IsolatedWebAppDownloader;
 
 // This component is responsible for installing, uninstalling, updating etc.
 // of the policy installed IWAs.
@@ -60,6 +61,7 @@
     virtual void Install(
         const IsolatedWebAppLocation& location,
         const IsolatedWebAppUrlInfo& url_info,
+        const base::Version& expected_version,
         WebAppCommandScheduler::InstallIsolatedWebAppCallback callback) = 0;
   };
 
@@ -68,6 +70,7 @@
     explicit IwaInstallCommandWrapperImpl(web_app::WebAppProvider* provider);
     void Install(const IsolatedWebAppLocation& location,
                  const IsolatedWebAppUrlInfo& url_info,
+                 const base::Version& expected_version,
                  WebAppCommandScheduler::InstallIsolatedWebAppCallback callback)
         override;
     ~IwaInstallCommandWrapperImpl() override = default;
@@ -116,9 +119,7 @@
 
   // Downloading of the Signed Web Bundle.
   void DownloadWebBundle();
-  void OnWebBundleDownloaded(
-      std::unique_ptr<network::SimpleURLLoader> simple_loader,
-      base::FilePath path);
+  void OnWebBundleDownloaded(const base::FilePath& path, int32_t net_error);
 
   // Installing of the IWA using the downloaded Signed Web Bundle.
   void InstallIwa(base::FilePath path);
@@ -140,6 +141,7 @@
       ephemeral_iwa_install_options_;
   std::vector<IsolatedWebAppExternalInstallOptions>::iterator current_app_;
   std::unique_ptr<UpdateManifestFetcher> current_update_manifest_fetcher_;
+  std::unique_ptr<IsolatedWebAppDownloader> current_bundle_downloader_;
 
   const base::FilePath installation_dir_;
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
index 0379e8ed..dbde8b50 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
@@ -194,7 +194,7 @@
           policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION));
   policy::UserPolicyBuilder device_local_account_policy_;
   const web_app::TestSignedWebBundle iwa_bundle_ =
-      web_app::BuildDefaultTestSignedWebBundle();
+      web_app::BuildDefaultTestSignedWebBundle(base::Version("7.0.6"));
 
  private:
   ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_unittest.cc
index 81501072..39c7f5b 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/values.h"
+#include "base/version.h"
 #include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_external_install_options.h"
@@ -168,9 +169,16 @@
   void Install(
       const IsolatedWebAppLocation& location,
       const IsolatedWebAppUrlInfo& url_info,
+      const base::Version& expected_version,
       WebAppCommandScheduler::InstallIsolatedWebAppCallback callback) override {
     if (url_info.web_bundle_id().id() == kWebBundleId1 ||
         url_info.web_bundle_id().id() == kWebBundleId2) {
+      if (url_info.web_bundle_id().id() == kWebBundleId1) {
+        EXPECT_EQ(expected_version, base::Version("7.0.6"));
+      } else if (url_info.web_bundle_id().id() == kWebBundleId2) {
+        EXPECT_EQ(expected_version, base::Version("3.0.0"));
+      }
+
       std::move(callback).Run(InstallIsolatedWebAppCommandSuccess{});
       return;
     }
diff --git a/chrome/browser/web_applications/proto/web_app.proto b/chrome/browser/web_applications/proto/web_app.proto
index 33ca7b74..1c1bf45 100644
--- a/chrome/browser/web_applications/proto/web_app.proto
+++ b/chrome/browser/web_applications/proto/web_app.proto
@@ -179,6 +179,9 @@
   // The partition_name of every <controlledframe> StoragePartition managed by
   // this Isolated Web App.
   repeated string controlled_frame_partitions = 4;
+
+  // The version of the IWA.
+  required string version = 5;
 }
 
 // Full WebApp object data. See detailed comments in
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index 0119833..ee5654d 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -210,7 +210,7 @@
       const auto origin =
           url::Origin::Create(GURL("https://app-" + suffix_str + ".com/"));
       permissions_policy[i].allowed_origins.emplace_back(
-          blink::OriginWithPossibleWildcards::FromOrigin(origin));
+          *blink::OriginWithPossibleWildcards::FromOrigin(origin));
     }
   }
   return permissions_policy;
@@ -917,8 +917,15 @@
     };
     static_assert(std::size(location_types) == kNumLocationTypes);
 
-    WebApp::IsolationData isolation_data(
-        location_types[random.next_uint(kNumLocationTypes)]);
+    IsolatedWebAppLocation location_type =
+        location_types[random.next_uint(kNumLocationTypes)];
+    base::Version version = base::Version({
+        random.next_uint(),
+        random.next_uint(),
+        random.next_uint(),
+    });
+
+    WebApp::IsolationData isolation_data(location_type, version);
     if (random.next_bool()) {
       isolation_data.controlled_frame_partitions.insert("partition_name");
     }
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index 0e187ff6..d21fc91 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -741,12 +741,15 @@
   return root;
 }
 
-WebApp::IsolationData::IsolationData(IsolatedWebAppLocation location)
-    : location(location) {}
+WebApp::IsolationData::IsolationData(IsolatedWebAppLocation location,
+                                     base::Version version)
+    : location(location), version(std::move(version)) {}
 WebApp::IsolationData::IsolationData(
     IsolatedWebAppLocation location,
+    base::Version version,
     const std::set<std::string>& controlled_frame_partitions)
     : location(location),
+      version(std::move(version)),
       controlled_frame_partitions(controlled_frame_partitions) {}
 WebApp::IsolationData::~IsolationData() = default;
 WebApp::IsolationData::IsolationData(const WebApp::IsolationData&) = default;
@@ -758,7 +761,7 @@
 
 bool WebApp::IsolationData::operator==(
     const WebApp::IsolationData& other) const {
-  return location == other.location &&
+  return location == other.location && version == other.version &&
          controlled_frame_partitions == other.controlled_frame_partitions;
 }
 bool WebApp::IsolationData::operator!=(
@@ -767,9 +770,10 @@
 }
 
 base::Value WebApp::IsolationData::AsDebugValue() const {
-  base::Value::Dict value;
-  value.Set("isolated_web_app_location",
-            IsolatedWebAppLocationAsDebugValue(location));
+  auto value = base::Value::Dict()
+                   .Set("isolated_web_app_location",
+                        IsolatedWebAppLocationAsDebugValue(location))
+                   .Set("version", version.GetString());
   base::Value::List* partitions =
       value.EnsureList("controlled_frame_partitions");
   for (const std::string& partition : controlled_frame_partitions) {
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index 24f0e0bf..1b1a6c3 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -13,6 +13,7 @@
 #include "base/containers/flat_set.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "base/version.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_location.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
@@ -332,8 +333,9 @@
   // If present, signals that this app is an Isolated Web App, and contains
   // IWA-specific information like bundle location.
   struct IsolationData {
-    explicit IsolationData(IsolatedWebAppLocation location);
+    IsolationData(IsolatedWebAppLocation location, base::Version version);
     IsolationData(IsolatedWebAppLocation location,
+                  base::Version version,
                   const std::set<std::string>& controlled_frame_partitions);
     ~IsolationData();
     IsolationData(const IsolationData&);
@@ -346,6 +348,7 @@
     base::Value AsDebugValue() const;
 
     IsolatedWebAppLocation location;
+    base::Version version;
     std::set<std::string> controlled_frame_partitions;
   };
   const absl::optional<IsolationData>& isolation_data() const {
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
index 6586f27..cd297af2 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.cc
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -12,6 +12,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "base/version.h"
 #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
 #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "chrome/browser/profiles/profile.h"
@@ -344,6 +345,7 @@
 void WebAppCommandScheduler::InstallIsolatedWebApp(
     const IsolatedWebAppUrlInfo& url_info,
     const IsolatedWebAppLocation& location,
+    const absl::optional<base::Version>& expected_version,
     std::unique_ptr<ScopedKeepAlive> optional_keep_alive,
     std::unique_ptr<ScopedProfileKeepAlive> optional_profile_keep_alive,
     InstallIsolatedWebAppCallback callback,
@@ -361,7 +363,8 @@
   }
   provider_->command_manager().ScheduleCommand(
       std::make_unique<InstallIsolatedWebAppCommand>(
-          url_info, location, CreateIsolatedWebAppWebContents(*profile_),
+          url_info, location, expected_version,
+          CreateIsolatedWebAppWebContents(*profile_),
           provider_->web_contents_manager().CreateUrlLoader(),
           std::move(optional_keep_alive),
           std::move(optional_profile_keep_alive), std::move(callback),
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
index d4ce955..dba56415 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.h
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -14,6 +14,7 @@
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "base/version.h"
 #include "chrome/browser/web_applications/commands/fetch_installability_for_chrome_management.h"
 #include "chrome/browser/web_applications/commands/manifest_update_check_command.h"
 #include "chrome/browser/web_applications/commands/manifest_update_finalize_command.h"
@@ -178,10 +179,13 @@
       const base::Location& location = FROM_HERE);
 
   // Schedules a command that installs the Isolated Web App described by the
-  // given IsolatedWebAppUrlInfo and IsolationData.
+  // given IsolatedWebAppUrlInfo and IsolationData. If `expected_version` is
+  // set, then this command will refuse to install the Isolated Web App if its
+  // version does not match.
   virtual void InstallIsolatedWebApp(
       const IsolatedWebAppUrlInfo& url_info,
       const IsolatedWebAppLocation& location,
+      const absl::optional<base::Version>& expected_version,
       std::unique_ptr<ScopedKeepAlive> optional_keep_alive,
       std::unique_ptr<ScopedProfileKeepAlive> optional_profile_keep_alive,
       InstallIsolatedWebAppCallback callback,
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index e6e0edc..c169cb44 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -19,6 +19,7 @@
 #include "base/pickle.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_location.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_version.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
 #include "chrome/browser/web_applications/proto/web_app.pb.h"
@@ -813,6 +814,8 @@
 
   if (web_app.isolation_data().has_value()) {
     auto* mutable_data = local_data->mutable_isolation_data();
+    mutable_data->set_version(web_app.isolation_data()->version.GetString());
+
     for (const std::string& partition :
          web_app.isolation_data()->controlled_frame_partitions) {
       mutable_data->add_controlled_frame_partitions(partition);
@@ -1512,6 +1515,16 @@
         local_data.isolation_data().controlled_frame_partitions();
     std::set<std::string> controlled_frame_partitions(partitions.begin(),
                                                       partitions.end());
+    auto version_components =
+        ParseIwaVersionIntoComponents(local_data.isolation_data().version());
+    if (!version_components.has_value()) {
+      DLOG(ERROR) << "WebApp proto isolation_data.version parse error: cannot "
+                     "deserialize version: "
+                  << IwaVersionParseErrorToString(version_components.error());
+      return nullptr;
+    }
+    base::Version version(
+        std::vector(version_components->begin(), version_components->end()));
 
     switch (local_data.isolation_data().location_case()) {
       case IsolationDataProto::LocationCase::kInstalledBundle: {
@@ -1522,8 +1535,9 @@
                          "parse error: cannot deserialize file path";
           return nullptr;
         }
-        web_app->SetIsolationData(WebApp::IsolationData(
-            InstalledBundle{.path = *path}, controlled_frame_partitions));
+        web_app->SetIsolationData(
+            WebApp::IsolationData(InstalledBundle{.path = *path}, version,
+                                  controlled_frame_partitions));
         break;
       }
 
@@ -1535,8 +1549,9 @@
                          "parse error: cannot deserialize file path";
           return nullptr;
         }
-        web_app->SetIsolationData(WebApp::IsolationData(
-            DevModeBundle{.path = *path}, controlled_frame_partitions));
+        web_app->SetIsolationData(
+            WebApp::IsolationData(DevModeBundle{.path = *path}, version,
+                                  controlled_frame_partitions));
         break;
       }
 
@@ -1551,8 +1566,9 @@
                      local_data.isolation_data().dev_mode_proxy().proxy_url();
           return nullptr;
         }
-        web_app->SetIsolationData(WebApp::IsolationData(
-            DevModeProxy{.proxy_url = proxy_url}, controlled_frame_partitions));
+        web_app->SetIsolationData(
+            WebApp::IsolationData(DevModeProxy{.proxy_url = proxy_url}, version,
+                                  controlled_frame_partitions));
         break;
       }
 
diff --git a/chrome/browser/web_applications/web_app_database_unittest.cc b/chrome/browser/web_applications/web_app_database_unittest.cc
index a9a58c9..879cbbc 100644
--- a/chrome/browser/web_applications/web_app_database_unittest.cc
+++ b/chrome/browser/web_applications/web_app_database_unittest.cc
@@ -621,21 +621,22 @@
 
 TEST_F(WebAppDatabaseProtoDataTest, SavesInstalledBundleIsolationData) {
   base::FilePath path(FILE_PATH_LITERAL("bundle_path"));
-  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(
-      WebApp::IsolationData(InstalledBundle{.path = path}));
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
+      InstalledBundle{.path = path}, base::Version("1.0.0")));
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
   EXPECT_THAT(*web_app, Eq(*protoed_web_app));
   EXPECT_THAT(web_app->isolation_data()->location,
               VariantWith<InstalledBundle>(
                   Field("path", &InstalledBundle::path, Eq(path))));
+  EXPECT_THAT(web_app->isolation_data()->version, Eq(base::Version("1.0.0")));
 }
 
 TEST_F(WebAppDatabaseProtoDataTest,
        HandlesCorruptedInstalledBundleIsolationData) {
   base::FilePath path(FILE_PATH_LITERAL("bundle_path"));
-  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(
-      WebApp::IsolationData(InstalledBundle{.path = path}));
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
+      InstalledBundle{.path = path}, base::Version("1.0.0")));
 
   std::unique_ptr<WebAppProto> web_app_proto =
       WebAppDatabase::CreateWebAppProto(*web_app);
@@ -655,21 +656,22 @@
 
 TEST_F(WebAppDatabaseProtoDataTest, SavesDevModeBundleIsolationData) {
   base::FilePath path(FILE_PATH_LITERAL("dev_bundle_path"));
-  std::unique_ptr<WebApp> web_app =
-      CreateIsolatedWebApp(WebApp::IsolationData(DevModeBundle{.path = path}));
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
+      DevModeBundle{.path = path}, base::Version("1.0.0")));
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
   EXPECT_THAT(*web_app, Eq(*protoed_web_app));
   EXPECT_THAT(web_app->isolation_data()->location,
               VariantWith<DevModeBundle>(
                   Field("path", &DevModeBundle::path, Eq(path))));
+  EXPECT_THAT(web_app->isolation_data()->version, Eq(base::Version("1.0.0")));
 }
 
 TEST_F(WebAppDatabaseProtoDataTest,
        HandlesCorruptedDevModeBundleIsolationData) {
   base::FilePath path(FILE_PATH_LITERAL("bundle_path"));
-  std::unique_ptr<WebApp> web_app =
-      CreateIsolatedWebApp(WebApp::IsolationData(DevModeBundle{.path = path}));
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
+      DevModeBundle{.path = path}, base::Version("1.0.0")));
 
   std::unique_ptr<WebAppProto> web_app_proto =
       WebAppDatabase::CreateWebAppProto(*web_app);
@@ -688,9 +690,10 @@
 }
 
 TEST_F(WebAppDatabaseProtoDataTest, SavesDevModeProxyIsolationData) {
-  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(
-      WebApp::IsolationData(DevModeProxy{.proxy_url = url::Origin::Create(GURL(
-                                             "https://proxy-example.com/"))}));
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
+      DevModeProxy{.proxy_url =
+                       url::Origin::Create(GURL("https://proxy-example.com/"))},
+      base::Version("1.0.0")));
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
   EXPECT_THAT(*web_app, Eq(*protoed_web_app));
@@ -699,6 +702,23 @@
       VariantWith<DevModeProxy>(
           Field("proxy_url", &DevModeProxy::proxy_url,
                 Eq(url::Origin::Create(GURL("https://proxy-example.com/"))))));
+  EXPECT_THAT(web_app->isolation_data()->version, Eq(base::Version("1.0.0")));
+}
+
+TEST_F(WebAppDatabaseProtoDataTest, HandlesCorruptedIsolationDataVersion) {
+  base::FilePath path(FILE_PATH_LITERAL("bundle_path"));
+  // The version must have three numeric parts, thus using five parts here
+  // should break deserialization.
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
+      InstalledBundle{.path = path}, base::Version("1.2.3.4.5")));
+
+  std::unique_ptr<WebAppProto> web_app_proto =
+      WebAppDatabase::CreateWebAppProto(*web_app);
+  ASSERT_THAT(web_app_proto, NotNull());
+
+  std::unique_ptr<WebApp> protoed_web_app =
+      WebAppDatabase::CreateWebApp(*web_app_proto);
+  EXPECT_THAT(protoed_web_app, IsNull());
 }
 
 TEST_F(WebAppDatabaseProtoDataTest, PermissionsPolicyRoundTrip) {
@@ -714,10 +734,10 @@
        /*matches_all_origins=*/true,
        /*matches_opaque_src=*/false},
       {blink::mojom::PermissionsPolicyFeature::kGamepad,
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             url::Origin::Create(GURL("https://example.com")),
             /*has_subdomain_wildcard=*/false),
-        blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        *blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             url::Origin::Create(GURL("https://example.net")),
             /*has_subdomain_wildcard=*/true)},
        /*self_if_matches=*/absl::nullopt,
@@ -744,15 +764,12 @@
        /*matches_all_origins=*/true,
        /*matches_opaque_src=*/false},
       {blink::mojom::PermissionsPolicyFeature::kGamepad,
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             url::Origin::Create(GURL("https://example.com")),
             /*has_subdomain_wildcard=*/false),
-        blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        *blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             url::Origin::Create(GURL("https://example.net")),
-            /*has_subdomain_wildcard=*/true),
-        blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
-            url::Origin::Create(GURL("https://*.example.net")),
-            /*has_subdomain_wildcard=*/false)},
+            /*has_subdomain_wildcard=*/true)},
        /*self_if_matches=*/absl::nullopt,
        /*matches_all_origins=*/false,
        /*matches_opaque_src=*/false},
@@ -771,13 +788,11 @@
   EXPECT_EQ(proto->permissions_policy().at(1).matches_all_origins(), true);
   EXPECT_EQ(proto->permissions_policy().at(1).matches_opaque_src(), false);
   EXPECT_EQ(proto->permissions_policy().at(2).feature(), "gamepad");
-  ASSERT_EQ(proto->permissions_policy().at(2).allowed_origins_size(), 3);
+  ASSERT_EQ(proto->permissions_policy().at(2).allowed_origins_size(), 2);
   EXPECT_EQ(proto->permissions_policy().at(2).allowed_origins(0),
             "https://example.com");
   EXPECT_EQ(proto->permissions_policy().at(2).allowed_origins(1),
             "https://*.example.net");
-  EXPECT_EQ(proto->permissions_policy().at(2).allowed_origins(2),
-            "https://%2A.example.net");
   EXPECT_EQ(proto->permissions_policy().at(2).matches_all_origins(), false);
   EXPECT_EQ(proto->permissions_policy().at(2).matches_opaque_src(), false);
 }
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index ce64d8c..d934414 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -235,8 +235,10 @@
 #endif
 
   if (options.isolated_web_app_location.has_value()) {
+    CHECK(web_app_info.isolated_web_app_version.IsValid());
     web_app->SetIsolationData(
-        WebApp::IsolationData(*options.isolated_web_app_location));
+        WebApp::IsolationData(*options.isolated_web_app_location,
+                              web_app_info.isolated_web_app_version));
   }
 
   web_app->SetParentAppId(web_app_info.parent_app_id);
diff --git a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
index 7f39eb9..4ae3ee4 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
@@ -476,9 +476,12 @@
 }
 
 TEST_P(WebAppInstallFinalizerUnitTest, IsolationDataSetInWebAppDB) {
+  base::Version version("1.2.3");
+
   WebAppInstallInfo info;
   info.start_url = GURL("https://foo.example");
   info.title = u"Foo Title";
+  info.isolated_web_app_version = version;
 
   const IsolatedWebAppLocation location =
       DevModeBundle{.path = base::FilePath(FILE_PATH_LITERAL("p"))};
@@ -494,6 +497,7 @@
 
   const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
   EXPECT_EQ(location, installed_app->isolation_data()->location);
+  EXPECT_EQ(version, installed_app->isolation_data()->version);
 }
 
 TEST_P(WebAppInstallFinalizerUnitTest, ValidateOriginAssociationsApproved) {
diff --git a/chrome/browser/web_applications/web_app_install_info.h b/chrome/browser/web_applications/web_app_install_info.h
index 93cdc2e..01eea408 100644
--- a/chrome/browser/web_applications/web_app_install_info.h
+++ b/chrome/browser/web_applications/web_app_install_info.h
@@ -14,6 +14,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/values.h"
+#include "base/version.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/scope_extension_info.h"
 #include "chrome/browser/web_applications/web_app_id.h"
@@ -370,6 +371,9 @@
   // policy_ids but rather just a supplement for tricky cases.
   std::vector<std::string> additional_policy_ids;
 
+  // Used to specify the version of an Isolated Web App that is being installed.
+  base::Version isolated_web_app_version;
+
  private:
   // Used this method in Clone() method. Use Clone() to deep copy explicitly.
   WebAppInstallInfo(const WebAppInstallInfo& other);
diff --git a/chrome/browser/web_applications/web_app_install_utils_unittest.cc b/chrome/browser/web_applications/web_app_install_utils_unittest.cc
index 70b2ba1..ef891bc 100644
--- a/chrome/browser/web_applications/web_app_install_utils_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_utils_unittest.cc
@@ -159,7 +159,7 @@
     blink::ParsedPermissionsPolicyDeclaration declaration;
     declaration.feature = blink::mojom::PermissionsPolicyFeature::kFullscreen;
     declaration.allowed_origins = {
-        blink::OriginWithPossibleWildcards::FromOrigin(
+        *blink::OriginWithPossibleWildcards::FromOrigin(
             url::Origin::Create(GURL("https://www.example.com")))};
     declaration.matches_all_origins = false;
     declaration.matches_opaque_src = false;
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index 59b1216..6800a4a 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -987,8 +987,8 @@
   const AppId app_id = isolated_web_app->app_id();
 
   isolated_web_app->SetScope(isolated_web_app->start_url());
-  isolated_web_app->SetIsolationData(
-      WebApp::IsolationData(InstalledBundle{.path = base::FilePath()}));
+  isolated_web_app->SetIsolationData(WebApp::IsolationData(
+      InstalledBundle{.path = base::FilePath()}, base::Version("1.0.0")));
   RegisterApp(std::move(isolated_web_app));
 
   std::vector<content::StoragePartitionConfig> storage_partition_configs =
@@ -1014,8 +1014,8 @@
   const AppId app_id = isolated_web_app->app_id();
 
   isolated_web_app->SetScope(isolated_web_app->start_url());
-  isolated_web_app->SetIsolationData(
-      WebApp::IsolationData(InstalledBundle{.path = base::FilePath()}));
+  isolated_web_app->SetIsolationData(WebApp::IsolationData(
+      InstalledBundle{.path = base::FilePath()}, base::Version("1.0.0")));
   isolated_web_app->SetIsLocallyInstalled(false);
   RegisterApp(std::move(isolated_web_app));
 
@@ -1102,8 +1102,10 @@
   web_app->SetDisplayMode(DisplayMode::kStandalone);
   web_app->SetUserDisplayMode(mojom::UserDisplayMode::kBrowser);
   web_app->SetIsLocallyInstalled(true);
-  web_app->SetIsolationData(WebApp::IsolationData(DevModeProxy{
-      .proxy_url = url::Origin::Create(GURL("http://127.0.0.1:8080"))}));
+  web_app->SetIsolationData(WebApp::IsolationData(
+      DevModeProxy{.proxy_url =
+                       url::Origin::Create(GURL("http://127.0.0.1:8080"))},
+      base::Version("1.0.0")));
 
   RegisterApp(std::move(web_app));
 
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index 996f397..201595e 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -341,8 +341,9 @@
 TEST(WebAppTest, IsolationDataDebugValue) {
   WebApp app{GenerateAppId(/*manifest_id=*/absl::nullopt,
                            GURL("https://example.com"))};
-  app.SetIsolationData(WebApp::IsolationData(InstalledBundle{
-      .path = base::FilePath(FILE_PATH_LITERAL("random_path"))}));
+  app.SetIsolationData(WebApp::IsolationData(
+      InstalledBundle{.path = base::FilePath(FILE_PATH_LITERAL("random_path"))},
+      base::Version("1.0.0")));
 
   EXPECT_TRUE(app.isolation_data().has_value());
 
@@ -352,6 +353,7 @@
             "path": "random_path"
           }
         },
+        "version": "1.0.0",
         "controlled_frame_partitions": []
       })")
                                             .value();
@@ -378,15 +380,12 @@
        /*matches_all_origins=*/true,
        /*matches_opaque_src=*/false},
       {blink::mojom::PermissionsPolicyFeature::kGamepad,
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             url::Origin::Create(GURL("https://example.com")),
             /*has_subdomain_wildcard=*/false),
-        blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        *blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             url::Origin::Create(GURL("https://example.net")),
-            /*has_subdomain_wildcard=*/true),
-        blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
-            url::Origin::Create(GURL("https://*.example.net")),
-            /*has_subdomain_wildcard=*/false)},
+            /*has_subdomain_wildcard=*/true)},
        /*self_if_matches=*/absl::nullopt,
        /*matches_all_origins=*/false,
        /*matches_opaque_src=*/false},
@@ -408,7 +407,7 @@
           "matches_opaque_src": false
         }
         , {
-          "allowed_origins": [ "https://example.com", "https://*.example.net", "https://%2A.example.net" ],
+          "allowed_origins": [ "https://example.com", "https://*.example.net" ],
           "feature": "gamepad",
           "matches_all_origins": false,
           "matches_opaque_src": false
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 8d2e8882..7c8c4c74 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1685663915-064c37fc1816bc4555275efe04e48ba6509af68b.profdata
+chrome-linux-main-1685707191-5c26d03ff1ce18327fe62f9630eae55f6a135fff.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 77d2b043..376c5a1f 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1685663915-8e33d907b423d057db8fdd63008fce34ec32d473.profdata
+chrome-mac-main-1685685276-65720f97b1192bc85bac11edd35e01a5dbdd0416.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 68e32ba..c9c4c8f 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1685663915-c897d968dfdf6b97ee97c610289eff5a054200e6.profdata
+chrome-win32-main-1685696392-4a2e3bd8263e83d509095ceee602118c865133c9.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 22d8dcb9..fa66a9b 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1685663915-9ac8b26bb7805d70320281353009a034bfae274c.profdata
+chrome-win64-main-1685696392-d6133a68790c0813c3e1ce31184bf691f9c40e32.profdata
diff --git a/chrome/test/data/web_apps/borderless_isolated_app/manifest.webmanifest b/chrome/test/data/web_apps/borderless_isolated_app/manifest.webmanifest
index 3d5395d..c9c9df6 100644
--- a/chrome/test/data/web_apps/borderless_isolated_app/manifest.webmanifest
+++ b/chrome/test/data/web_apps/borderless_isolated_app/manifest.webmanifest
@@ -1,5 +1,6 @@
 {
   "name": "Borderless Isolated Web App",
+  "version": "1.0.0",
   "id": "/",
   "scope": "/",
   "start_url": "/index.html",
diff --git a/chrome/test/data/web_apps/sample_web_app.json b/chrome/test/data/web_apps/sample_web_app.json
index daadfeda..469fe79 100644
--- a/chrome/test/data/web_apps/sample_web_app.json
+++ b/chrome/test/data/web_apps/sample_web_app.json
@@ -194,7 +194,8 @@
          "dev_mode_bundle": {
             "path": "1234"
          }
-      }
+      },
+      "version": "2831420433.2572177745.363425051"
    },
    "last_badging_time": "1970-02-06 10:58:36.765 UTC",
    "last_launch_time": "1970-01-28 05:07:28.623 UTC",
diff --git a/chrome/test/data/web_apps/simple_isolated_app/manifest.webmanifest b/chrome/test/data/web_apps/simple_isolated_app/manifest.webmanifest
index 7dfe2c9..331b4cc9 100644
--- a/chrome/test/data/web_apps/simple_isolated_app/manifest.webmanifest
+++ b/chrome/test/data/web_apps/simple_isolated_app/manifest.webmanifest
@@ -1,5 +1,6 @@
 {
   "name": "Simple Isolated App",
+  "version": "1.0.0",
   "id": "/",
   "scope": "/",
   "start_url": "/index.html",
diff --git a/chromeos/ash/components/drivefs/drivefs_auth.cc b/chromeos/ash/components/drivefs/drivefs_auth.cc
index f5439d3..5db78f8 100644
--- a/chromeos/ash/components/drivefs/drivefs_auth.cc
+++ b/chromeos/ash/components/drivefs/drivefs_auth.cc
@@ -10,6 +10,7 @@
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -63,7 +64,9 @@
   timer_->Start(
       FROM_HERE, base::Seconds(30),
       base::BindOnce(&DriveFsAuth::AuthTimeout, base::Unretained(this)));
-  std::set<std::string> scopes({"https://www.googleapis.com/auth/drive"});
+  std::set<std::string> scopes(
+      {GaiaConstants::kDriveOAuth2Scope,
+       GaiaConstants::kExperimentsAndConfigsOAuth2Scope});
   access_token_fetcher_ =
       std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
           kIdentityConsumerId, identity_manager, scopes,
diff --git a/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.cc b/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.cc
index abf809f..940c3709 100644
--- a/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.cc
+++ b/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.cc
@@ -483,7 +483,9 @@
       callback_delay_);
 }
 
-void FakeCrosHealthd::RunMemoryRoutine(RunMemoryRoutineCallback callback) {
+void FakeCrosHealthd::RunMemoryRoutine(
+    absl::optional<uint32_t> max_testing_mem_kib,
+    RunMemoryRoutineCallback callback) {
   actual_passed_parameters_.clear();
   last_run_routine_ = mojom::DiagnosticRoutineEnum::kMemory;
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
diff --git a/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h b/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h
index b6b3f71..7bf5673 100644
--- a/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h
+++ b/chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h
@@ -235,7 +235,8 @@
       uint32_t length_seconds,
       uint32_t minimum_charge_percent_required,
       RunBatteryChargeRoutineCallback callback) override;
-  void RunMemoryRoutine(RunMemoryRoutineCallback callback) override;
+  void RunMemoryRoutine(absl::optional<uint32_t> max_testing_mem_kib,
+                        RunMemoryRoutineCallback callback) override;
   void RunLanConnectivityRoutine(
       RunLanConnectivityRoutineCallback callback) override;
   void RunSignalStrengthRoutine(
diff --git a/chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom b/chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom
index 9834edf5..ffafbfaf 100644
--- a/chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom
+++ b/chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom
@@ -54,7 +54,7 @@
 
 // Diagnostics interface exposed by the cros_healthd daemon.
 //
-// NextMinVersion: 10, NextIndex: 45
+// NextMinVersion: 11, NextIndex: 45
 [Stable]
 interface CrosHealthdDiagnosticsService {
   // Returns an array of all diagnostic routines that the platform supports.
@@ -350,10 +350,17 @@
   // The availability of this routine can be determined by checking that
   // kMemory is returned by GetAvailableRoutines.
   //
+  // The request:
+  // * |max_testing_mem_kib| - An optional uint32 to specify at most how much of
+  //                           the memory should be tested. If the value is
+  //                           null, memory test will run with as much memory as
+  //                           possible.
+  //
   // The response:
   // * |response| - contains a unique identifier and status for the created
   //                routine.
-  RunMemoryRoutine@16() => (RunRoutineResponse response);
+  RunMemoryRoutine@16([MinVersion=10] uint32? max_testing_mem_kib)
+      => (RunRoutineResponse response);
 
   // Requests that the LanConnectivity routine is created and started on the
   // platform. This routine checks whether the device is connected to a LAN.
diff --git a/chromeos/components/kiosk/BUILD.gn b/chromeos/components/kiosk/BUILD.gn
index 28d0798f..eb2e69c 100644
--- a/chromeos/components/kiosk/BUILD.gn
+++ b/chromeos/components/kiosk/BUILD.gn
@@ -20,3 +20,21 @@
     deps += [ "//chromeos/startup" ]
   }
 }
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "kiosk_test_utils.cc",
+    "kiosk_test_utils.h",
+  ]
+  deps = [ "//build:chromeos_buildflags" ]
+  if (is_chromeos) {
+    deps += [ "//chromeos/crosapi/mojom" ]
+  }
+  if (is_chromeos_ash) {
+    deps += [ "//chromeos/ash/components/login/login_state:login_state" ]
+  }
+  if (is_chromeos_lacros) {
+    deps += [ "//chromeos/startup" ]
+  }
+}
diff --git a/chromeos/components/kiosk/kiosk_test_utils.cc b/chromeos/components/kiosk/kiosk_test_utils.cc
new file mode 100644
index 0000000..ef90971
--- /dev/null
+++ b/chromeos/components/kiosk/kiosk_test_utils.cc
@@ -0,0 +1,38 @@
+// 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/components/kiosk/kiosk_test_utils.h"
+
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chromeos/ash/components/login/login_state/login_state.h"  // nogncheck
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/startup/browser_init_params.h"  // nogncheck
+#endif
+
+namespace chromeos {
+
+void SetUpFakeKioskSession() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::LoginState::Initialize();
+  ash::LoginState::Get()->SetLoggedInState(
+      ash::LoginState::LoggedInState::LOGGED_IN_ACTIVE,
+      ash::LoginState::LoggedInUserType::LOGGED_IN_USER_KIOSK);
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+  crosapi::mojom::BrowserInitParamsPtr init_params =
+      chromeos::BrowserInitParams::GetForTests()->Clone();
+  init_params->session_type = crosapi::mojom::SessionType::kWebKioskSession;
+  chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
+#endif
+}
+
+void TearDownFakeKioskSession() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::LoginState::Shutdown();
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+}
+
+}  // namespace chromeos
diff --git a/chromeos/components/kiosk/kiosk_test_utils.h b/chromeos/components/kiosk/kiosk_test_utils.h
new file mode 100644
index 0000000..99b6e338
--- /dev/null
+++ b/chromeos/components/kiosk/kiosk_test_utils.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 CHROMEOS_COMPONENTS_KIOSK_KIOSK_TEST_UTILS_H_
+#define CHROMEOS_COMPONENTS_KIOSK_KIOSK_TEST_UTILS_H_
+
+namespace chromeos {
+
+// Sets up a fake kiosk session for unit tests.
+extern void SetUpFakeKioskSession();
+
+// Tears down a fake kiosk session for unit tests.
+extern void TearDownFakeKioskSession();
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_KIOSK_KIOSK_TEST_UTILS_H_
diff --git a/components/autofill/content/browser/form_forest_unittest.cc b/components/autofill/content/browser/form_forest_unittest.cc
index ffb57e4..50f794d 100644
--- a/components/autofill/content/browser/form_forest_unittest.cc
+++ b/components/autofill/content/browser/form_forest_unittest.cc
@@ -400,7 +400,7 @@
       url::Origin origin) {
     return {blink::ParsedPermissionsPolicyDeclaration(
         blink::mojom::PermissionsPolicyFeature::kSharedAutofill,
-        {blink::OriginWithPossibleWildcards::FromOrigin(origin)},
+        {*blink::OriginWithPossibleWildcards::FromOrigin(origin)},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false)};
@@ -1647,15 +1647,12 @@
 }
 
 // Tests irreflexivity, asymmetry, transitivity of FrameData less-than relation.
-TEST(FormForestTest, FrameDataComparator) {
+TEST_F(FormForestTest, FrameDataComparator) {
   FrameData::CompareByFrameToken less;
   std::unique_ptr<FrameData> null;
   auto x = std::make_unique<FrameData>(test::MakeLocalFrameToken());
-  auto xx = std::make_unique<FrameData>(test::MakeLocalFrameToken());
-  auto y = std::make_unique<FrameData>(
-      LocalFrameToken(base::UnguessableToken::CreateForTesting(
-          x->frame_token->GetHighForSerialization() + 1,
-          x->frame_token->GetLowForSerialization() + 1)));
+  auto xx = std::make_unique<FrameData>(x->frame_token);
+  auto y = std::make_unique<FrameData>(test::MakeLocalFrameToken());
   ASSERT_TRUE(x->frame_token < y->frame_token);
   EXPECT_FALSE(less(null, null));
   EXPECT_TRUE(less(null, x));
diff --git a/components/autofill/core/browser/autofill_feedback_data_unittest.cc b/components/autofill/core/browser/autofill_feedback_data_unittest.cc
index 3c317f9..691e89c8 100644
--- a/components/autofill/core/browser/autofill_feedback_data_unittest.cc
+++ b/components/autofill/core/browser/autofill_feedback_data_unittest.cc
@@ -23,7 +23,7 @@
 const char kExpectedFeedbackDataJSON[] = R"({
    "formStructures": [ {
       "formSignature": "4232380759432074174",
-      "hostFrame": "00000000000000000000000000000000",
+      "hostFrame": "00000000000181CD000000000000A8CA",
       "idAttribute": "",
       "mainFrameUrl": "https://myform_root.com",
       "nameAttribute": "",
@@ -97,6 +97,7 @@
 })";
 
 void CreateFeedbackTestFormData(FormData* form) {
+  form->host_frame = test::MakeLocalFrameToken(test::RandomizeFrame(false));
   form->unique_renderer_id = test::MakeFormRendererId();
   form->name = u"MyForm";
   form->url = GURL("https://myform.com/form.html");
@@ -107,11 +108,14 @@
   FormFieldData field;
   test::CreateTestFormField("First Name on Card", "firstnameoncard", "", "text",
                             "cc-given-name", &field);
+  field.host_frame = form->host_frame;
   form->fields.push_back(field);
   test::CreateTestFormField("Last Name on Card", "lastnameoncard", "", "text",
                             "cc-family-name", &field);
+  field.host_frame = form->host_frame;
   form->fields.push_back(field);
   test::CreateTestFormField("Email", "email", "", "email", &field);
+  field.host_frame = form->host_frame;
   form->fields.push_back(field);
 }
 }  // namespace
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index b1ab031..6bcd0b9c 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -123,10 +123,15 @@
       const Options& options = {.disable_server_communication = false});
 };
 
-// Creates non-empty LocalFrameToken. If `randomize` is false, the
-// LocalFrameToken is stable across multiple calls.
+// Creates non-empty LocalFrameToken.
+//
+// If `randomize` is true, the LocalFrameToken changes for successive calls.
+// Within each unit test, the generated values are deterministically predictable
+// (because the test's AutofillTestEnvironment restarts the generation).
+//
+// If `randomize` is false, the LocalFrameToken is stable across multiple calls.
 LocalFrameToken MakeLocalFrameToken(
-    RandomizeFrame randomize = RandomizeFrame(false));
+    RandomizeFrame randomize = RandomizeFrame(true));
 
 // Creates new, pairwise distinct FormRendererIds.
 inline FormRendererId MakeFormRendererId() {
@@ -142,14 +147,14 @@
 // LocalFrameToken is generated randomly, otherwise it is stable across multiple
 // calls.
 inline FormGlobalId MakeFormGlobalId(
-    RandomizeFrame randomize = RandomizeFrame(false)) {
+    RandomizeFrame randomize = RandomizeFrame(true)) {
   return {MakeLocalFrameToken(randomize), MakeFormRendererId()};
 }
 
 // Creates new, pairwise distinct FieldGlobalIds. If `randomize` is true, the
 // LocalFrameToken is generated randomly, otherwise it is stable.
 inline FieldGlobalId MakeFieldGlobalId(
-    RandomizeFrame randomize = RandomizeFrame(false)) {
+    RandomizeFrame randomize = RandomizeFrame(true)) {
   return {MakeLocalFrameToken(randomize), MakeFieldRendererId()};
 }
 
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index cc57890..da59f50 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -3843,7 +3843,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities;
   FormData form;
-  form.host_frame = test::MakeLocalFrameToken(test::RandomizeFrame(true));
+  form.host_frame = test::MakeLocalFrameToken();
   form.url = GURL("http://www.foo.com/");
   form.is_form_tag = true;
 
@@ -3865,7 +3865,7 @@
   test::InitializePossibleTypesAndValidities(possible_field_types,
                                              possible_field_types_validities,
                                              {CREDIT_CARD_NUMBER});
-  field.host_frame = test::MakeLocalFrameToken(test::RandomizeFrame(true));
+  field.host_frame = test::MakeLocalFrameToken();
   field.unique_renderer_id = test::MakeFieldRendererId();
   field.host_form_signature = FormSignature(456);
   form.fields.push_back(field);
@@ -3885,7 +3885,7 @@
   test::InitializePossibleTypesAndValidities(possible_field_types,
                                              possible_field_types_validities,
                                              {CREDIT_CARD_VERIFICATION_CODE});
-  field.host_frame = test::MakeLocalFrameToken(test::RandomizeFrame(true));
+  field.host_frame = test::MakeLocalFrameToken();
   field.unique_renderer_id = test::MakeFieldRendererId();
   field.host_form_signature = FormSignature(456);
   form.fields.push_back(field);
@@ -6333,7 +6333,7 @@
   EXPECT_EQ("blue-shipping", form_structure.field(0)->section.ToString());
   EXPECT_EQ("blue-shipping", form_structure.field(1)->section.ToString());
   EXPECT_EQ("blue-shipping", form_structure.field(2)->section.ToString());
-  EXPECT_EQ("country_0_14", form_structure.field(3)->section.ToString());
+  EXPECT_EQ("country_2_14", form_structure.field(3)->section.ToString());
 }
 
 // Tests if a new logical form is started with the second appearance of a field
@@ -6384,7 +6384,7 @@
   EXPECT_EQ("blue-shipping", form_structure.field(0)->section.ToString());
   EXPECT_EQ("blue-billing", form_structure.field(1)->section.ToString());
   EXPECT_EQ("blue-billing", form_structure.field(2)->section.ToString());
-  EXPECT_EQ("country_0_14", form_structure.field(3)->section.ToString());
+  EXPECT_EQ("country_2_14", form_structure.field(3)->section.ToString());
 }
 
 // Tests if a new logical form is started with the second appearance of a field
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index eb04345..11d62a5 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -272,7 +272,10 @@
   base::flat_map<LocalFrameToken, size_t> frame_token_ids;
   FormFieldData field;
   field.name = u"from_field_name";
-  field.host_frame = test::MakeLocalFrameToken();
+  // Randomizing the LocalFrameToken requires an AutofillTestEnvironment, which
+  // doesn't exist yet because SectionTestCases() is called by
+  // INSTANTIATE_TEST_SUITE_P().
+  field.host_frame = test::MakeLocalFrameToken(test::RandomizeFrame(false));
   field.unique_renderer_id = FieldRendererId(123);
   s = Section::FromFieldIdentifier(field, frame_token_ids);
   test_cases.push_back(s);
diff --git a/components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java b/components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java
index 03846977..0e16b341 100644
--- a/components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java
+++ b/components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java
@@ -520,6 +520,7 @@
             mState = newState;
         } else {
             if (!(mState == State.CANCELLED || mState == State.ERROR)) {
+                // TODO(crbug/1450573): Use Enums for state instead for better error messages.
                 throw new IllegalStateException(
                         "Invalid state transition - expected " + expected + " but was " + mState);
             }
diff --git a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetControllerTest.java b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetControllerTest.java
index 6e9b9842..45e8902 100644
--- a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetControllerTest.java
+++ b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetControllerTest.java
@@ -6,8 +6,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
 import android.content.Context;
@@ -47,7 +45,7 @@
     @SmallTest
     public void testGetFakeCronetEnginesStartsEmpty() {
         List<CronetEngine> engines = FakeCronetController.getFakeCronetEngines();
-        assertEquals(0, engines.size());
+        assertThat(engines).isEmpty();
     }
 
     @Test
@@ -81,21 +79,21 @@
         FakeCronetProvider provider = new FakeCronetProvider(mContext);
         CronetEngine providerEngine = provider.createBuilder().build();
 
-        assertEquals(
-                mFakeCronetController, FakeCronetController.getControllerForFakeEngine(engine));
-        assertEquals(
-                mFakeCronetController, FakeCronetController.getControllerForFakeEngine(engine2));
-        assertEquals(newController,
-                FakeCronetController.getControllerForFakeEngine(newControllerEngine));
-        assertEquals(newController,
-                FakeCronetController.getControllerForFakeEngine(newControllerEngine2));
+        assertThat(FakeCronetController.getControllerForFakeEngine(engine))
+                .isEqualTo(mFakeCronetController);
+        assertThat(FakeCronetController.getControllerForFakeEngine(engine2))
+                .isEqualTo(mFakeCronetController);
+        assertThat(FakeCronetController.getControllerForFakeEngine(newControllerEngine))
+                .isEqualTo(newController);
+        assertThat(FakeCronetController.getControllerForFakeEngine(newControllerEngine2))
+                .isEqualTo(newController);
 
         // TODO(kirchman): Test which controller the provider-created engine uses once the fake
         // UrlRequest class has been implemented.
-        assertNotEquals(mFakeCronetController,
-                FakeCronetController.getControllerForFakeEngine(providerEngine));
-        assertNotEquals(
-                newController, FakeCronetController.getControllerForFakeEngine(providerEngine));
+        assertThat(FakeCronetController.getControllerForFakeEngine(providerEngine))
+                .isNotEqualTo(mFakeCronetController);
+        assertThat(FakeCronetController.getControllerForFakeEngine(providerEngine))
+                .isNotEqualTo(newController);
         assertThat(FakeCronetController.getControllerForFakeEngine(providerEngine)).isNotNull();
     }
 
@@ -108,7 +106,8 @@
             FakeCronetController.getControllerForFakeEngine(javaEngine);
             fail("Should not be able to get a controller for a non-fake CronetEngine.");
         } catch (IllegalArgumentException e) {
-            assertEquals("Provided CronetEngine is not a fake CronetEngine", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "Provided CronetEngine is not a fake CronetEngine");
         }
     }
 
@@ -139,7 +138,7 @@
         FakeUrlResponse foundResponse =
                 mFakeCronetController.getResponse(new String(url), null, null, null);
 
-        assertEquals(response, foundResponse);
+        assertThat(foundResponse).isEqualTo(response);
     }
 
     @Test
@@ -154,8 +153,8 @@
 
         FakeUrlResponse foundResponse = mFakeCronetController.getResponse(url, null, null, null);
 
-        assertEquals(404, foundResponse.getHttpStatusCode());
-        assertNotEquals(response, foundResponse);
+        assertThat(foundResponse.getHttpStatusCode()).isEqualTo(404);
+        assertThat(foundResponse).isNotEqualTo(response);
     }
 
     @Test
@@ -170,8 +169,8 @@
 
         FakeUrlResponse foundResponse = mFakeCronetController.getResponse(url, null, null, null);
 
-        assertEquals(404, foundResponse.getHttpStatusCode());
-        assertNotEquals(response, foundResponse);
+        assertThat(foundResponse.getHttpStatusCode()).isEqualTo(404);
+        assertThat(foundResponse).isNotEqualTo(response);
     }
 
     @Test
@@ -184,7 +183,7 @@
 
         FakeUrlResponse foundResponse = mFakeCronetController.getResponse(url, null, null, null);
 
-        assertEquals(foundResponse, response);
+        assertThat(foundResponse).isEqualTo(response);
     }
 
     @Test
@@ -192,7 +191,7 @@
     public void testDefaultResponseIs404() {
         FakeUrlResponse foundResponse = mFakeCronetController.getResponse("url", null, null, null);
 
-        assertEquals(404, foundResponse.getHttpStatusCode());
+        assertThat(foundResponse.getHttpStatusCode()).isEqualTo(404);
     }
 
     @Test
@@ -218,7 +217,7 @@
 
         FakeUrlResponse foundResponse = mFakeCronetController.getResponse(url, null, null, null);
 
-        assertEquals(foundResponse.getHttpStatusCode(), httpStatusCode);
+        assertThat(foundResponse.getHttpStatusCode()).isEqualTo(httpStatusCode);
     }
 
     @Test
@@ -229,8 +228,8 @@
             mFakeCronetController.addHttpErrorResponse(nonErrorCode, "url");
             fail("Should not be able to add an error response with a non-error code.");
         } catch (IllegalArgumentException e) {
-            assertEquals("Expected HTTP error code (code >= 400), but was: " + nonErrorCode,
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "Expected HTTP error code (code >= 400), but was: " + nonErrorCode);
         }
     }
 
@@ -244,6 +243,6 @@
         FakeUrlResponse foundResponse = mFakeCronetController.getResponse(url, null, null, null);
 
         assertThat(foundResponse.getHttpStatusCode()).isIn(Range.closedOpen(200, 300));
-        assertEquals(body, new String(foundResponse.getResponseBody()));
+        assertThat(new String(foundResponse.getResponseBody())).isEqualTo(body);
     }
 }
diff --git a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetEngineTest.java b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetEngineTest.java
index 16aa17c..360334f 100644
--- a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetEngineTest.java
+++ b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetEngineTest.java
@@ -6,7 +6,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
@@ -86,9 +85,8 @@
             mFakeCronetEngine.newUrlRequestBuilder("", mCallback, mExecutor).build();
             fail("newUrlRequestBuilder API not checked for shutdown engine.");
         } catch (IllegalStateException e) {
-            assertEquals(
-                    "This instance of CronetEngine has been shutdown and can no longer be used.",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "This instance of CronetEngine has been shutdown and can no longer be used.");
         }
     }
 
@@ -101,9 +99,8 @@
             mFakeCronetEngine.newBidirectionalStreamBuilder("", null, null);
             fail("newBidirectionalStreamBuilder API not checked for shutdown engine.");
         } catch (IllegalStateException e) {
-            assertEquals(
-                    "This instance of CronetEngine has been shutdown and can no longer be used.",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "This instance of CronetEngine has been shutdown and can no longer be used.");
         }
     }
 
@@ -114,9 +111,9 @@
             mFakeCronetEngine.newBidirectionalStreamBuilder("", null, null);
             fail("newBidirectionalStreamBuilder API should not be available.");
         } catch (UnsupportedOperationException e) {
-            assertEquals("The bidirectional stream API is not supported by the Fake implementation "
-                            + "of CronetEngine.",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "The bidirectional stream API is not supported by the Fake implementation "
+                    + "of CronetEngine.");
         }
     }
 
@@ -127,9 +124,9 @@
             mFakeCronetEngine.openConnection(null);
             fail("openConnection API should not be available.");
         } catch (Exception e) {
-            assertEquals("The openConnection API is not supported by the Fake implementation of "
-                            + "CronetEngine.",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "The openConnection API is not supported by the Fake implementation of "
+                    + "CronetEngine.");
         }
     }
 
@@ -140,9 +137,9 @@
             mFakeCronetEngine.openConnection(null, Proxy.NO_PROXY);
             fail("openConnection API  should not be available.");
         } catch (Exception e) {
-            assertEquals("The openConnection API is not supported by the Fake implementation of "
-                            + "CronetEngine.",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "The openConnection API is not supported by the Fake implementation of "
+                    + "CronetEngine.");
         }
     }
 
@@ -153,18 +150,17 @@
             mFakeCronetEngine.createURLStreamHandlerFactory();
             fail("createURLStreamHandlerFactory API  should not be available.");
         } catch (UnsupportedOperationException e) {
-            assertEquals(
+            assertThat(e).hasMessageThat().isEqualTo(
                     "The URLStreamHandlerFactory API is not supported by the Fake implementation of"
-                            + " CronetEngine.",
-                    e.getMessage());
+                    + " CronetEngine.");
         }
     }
 
     @Test
     @SmallTest
     public void testGetVersionString() {
-        assertEquals("FakeCronet/" + ImplVersion.getCronetVersionWithLastChange(),
-                mFakeCronetEngine.getVersionString());
+        assertThat(mFakeCronetEngine.getVersionString())
+                .isEqualTo("FakeCronet/" + ImplVersion.getCronetVersionWithLastChange());
     }
 
     @Test
@@ -194,28 +190,29 @@
     @Test
     @SmallTest
     public void testGetEffectiveConnectionType() {
-        assertEquals(FakeCronetEngine.EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
-                mFakeCronetEngine.getEffectiveConnectionType());
+        assertThat(mFakeCronetEngine.getEffectiveConnectionType())
+                .isEqualTo(FakeCronetEngine.EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
     }
 
     @Test
     @SmallTest
     public void testGetHttpRttMs() {
-        assertEquals(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN, mFakeCronetEngine.getHttpRttMs());
+        assertThat(mFakeCronetEngine.getHttpRttMs())
+                .isEqualTo(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN);
     }
 
     @Test
     @SmallTest
     public void testGetTransportRttMs() {
-        assertEquals(
-                FakeCronetEngine.CONNECTION_METRIC_UNKNOWN, mFakeCronetEngine.getTransportRttMs());
+        assertThat(mFakeCronetEngine.getTransportRttMs())
+                .isEqualTo(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN);
     }
 
     @Test
     @SmallTest
     public void testGetDownstreamThroughputKbps() {
-        assertEquals(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN,
-                mFakeCronetEngine.getDownstreamThroughputKbps());
+        assertThat(mFakeCronetEngine.getDownstreamThroughputKbps())
+                .isEqualTo(FakeCronetEngine.CONNECTION_METRIC_UNKNOWN);
     }
 
     @Test
@@ -257,7 +254,7 @@
             mFakeCronetEngine.shutdown();
             fail("Shutdown not checked for running requests.");
         } catch (IllegalStateException e) {
-            assertEquals("Cannot shutdown with running requests.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Cannot shutdown with running requests.");
         }
 
         // Finish the request and verify the engine can be shutdown.
@@ -311,7 +308,7 @@
             @Override
             public void onRequestFinished(RequestFinishedInfo requestInfo) {
                 super.onRequestFinished(requestInfo);
-                assertEquals(url, requestInfo.getUrl());
+                assertThat(requestInfo.getUrl()).isEqualTo(url);
                 assertThat(requestInfo.getAnnotations()).contains(annotation);
             }
         };
@@ -336,8 +333,9 @@
             @Override
             public void onRequestFinished(RequestFinishedInfo requestInfo) {
                 super.onRequestFinished(requestInfo);
-                assertEquals("Exception received from UrlRequest.Callback",
-                        requestInfo.getException().getMessage());
+                assertThat(requestInfo.getException())
+                        .hasMessageThat()
+                        .isEqualTo("Exception received from UrlRequest.Callback");
             }
         };
         mFakeCronetEngine.addRequestFinishedListener(listener);
@@ -363,9 +361,9 @@
             mFakeCronetEngine.onRequestDestroyed();
             fail("onRequestDestroyed not checked for shutdown engine");
         } catch (IllegalStateException e) {
-            assertEquals("This instance of CronetEngine was shutdown. All requests must have been "
-                            + "complete.",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "This instance of CronetEngine was shutdown. All requests must have been "
+                    + "complete.");
         }
     }
 }
diff --git a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetProviderTest.java b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetProviderTest.java
index a4f8f069..ffa6811 100644
--- a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetProviderTest.java
+++ b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeCronetProviderTest.java
@@ -6,7 +6,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -39,13 +38,13 @@
     @SmallTest
     public void testGetName() {
         String expectedName = "Fake-Cronet-Provider";
-        assertEquals(expectedName, mProvider.getName());
+        assertThat(mProvider.getName()).isEqualTo(expectedName);
     }
 
     @Test
     @SmallTest
     public void testGetVersion() {
-        assertEquals(ImplVersion.getCronetVersion(), mProvider.getVersion());
+        assertThat(mProvider.getVersion()).isEqualTo(ImplVersion.getCronetVersion());
     }
 
     @Test
@@ -58,7 +57,7 @@
     @SmallTest
     public void testHashCode() {
         FakeCronetProvider otherProvider = new FakeCronetProvider(mContext);
-        assertEquals(otherProvider.hashCode(), mProvider.hashCode());
+        assertThat(mProvider.hashCode()).isEqualTo(otherProvider.hashCode());
     }
 
     @Test
diff --git a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlRequestTest.java b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlRequestTest.java
index 40a9f38..993b589 100644
--- a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlRequestTest.java
+++ b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlRequestTest.java
@@ -5,8 +5,8 @@
 package org.chromium.net.test;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -17,6 +17,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.google.common.collect.Maps;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -61,7 +63,7 @@
         request.getStatus(new StatusListener() {
             @Override
             public void onStatus(int status) {
-                assertEquals(expectedStatus, status);
+                assertThat(status).isEqualTo(expectedStatus);
                 foundStatus.open();
             }
         });
@@ -74,9 +76,10 @@
         for (int i = 1; i <= numberOfTimes; i++) {
             callback.startNextRead(request);
             callback.waitForNextStep();
-            assertEquals(
-                    "Expected read to happen " + numberOfTimes + " times but got " + i + " times.",
-                    ResponseStep.ON_READ_COMPLETED, callback.mResponseStep);
+            assertWithMessage(
+                    "Expected read to happen " + numberOfTimes + " times but got " + i + " times.")
+                    .that(callback.mResponseStep)
+                    .isEqualTo(ResponseStep.ON_READ_COMPLETED);
         }
     }
 
@@ -133,18 +136,18 @@
         callback.waitForNextStep();
 
         // Verify correct callback methods called and correct response returned.
-        assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
         callback.startNextRead(request);
         callback.waitForNextStep();
 
-        assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED);
         callback.startNextRead(request);
         callback.waitForNextStep();
 
-        assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
         callback.blockForDone();
 
-        assertEquals(callback.mResponseAsString, responseText);
+        assertThat(responseText).isEqualTo(callback.mResponseAsString);
     }
 
     @Test
@@ -155,19 +158,19 @@
             mFakeCronetEngine.newUrlRequestBuilder(null, callback, callback.getExecutor());
             fail("URL not null-checked");
         } catch (NullPointerException e) {
-            assertEquals("URL is required.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("URL is required.");
         }
         try {
             mFakeCronetEngine.newUrlRequestBuilder("url", null, callback.getExecutor());
             fail("Callback not null-checked");
         } catch (NullPointerException e) {
-            assertEquals("Callback is required.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Callback is required.");
         }
         try {
             mFakeCronetEngine.newUrlRequestBuilder("url", callback, null);
             fail("Executor not null-checked");
         } catch (NullPointerException e) {
-            assertEquals("Executor is required.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Executor is required.");
         }
         // Verify successful creation doesn't throw.
         mFakeCronetEngine.newUrlRequestBuilder("url", callback, callback.getExecutor());
@@ -186,7 +189,7 @@
             request.setHttpMethod(null);
             fail("Method not null-checked");
         } catch (NullPointerException e) {
-            assertEquals("Method is required.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Method is required.");
         }
     }
 
@@ -205,7 +208,7 @@
             request.setHttpMethod(method);
             fail("Method not checked for validity");
         } catch (IllegalArgumentException e) {
-            assertEquals("Invalid http method: " + method, e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Invalid http method: " + method);
         }
     }
 
@@ -226,7 +229,7 @@
             @Override
             public FakeUrlResponse getMatchingResponse(String url, String httpMethod,
                     List<Map.Entry<String, String>> headers, byte[] body) {
-                assertEquals(testMethod, httpMethod);
+                assertThat(httpMethod).isEqualTo(testMethod);
                 foundMethod.set(true);
                 // It doesn't matter if a response is actually returned.
                 return null;
@@ -261,9 +264,7 @@
             @Override
             public FakeUrlResponse getMatchingResponse(String url, String httpMethod,
                     List<Map.Entry<String, String>> headers, byte[] body) {
-                assertEquals(1, headers.size());
-                assertEquals(headerKey, headers.get(0).getKey());
-                assertEquals(headerValue, headers.get(0).getValue());
+                assertThat(headers).containsExactly(Maps.immutableEntry(headerKey, headerValue));
                 foundEntry.set(true);
                 // It doesn't matter if a response is actually returned.
                 return null;
@@ -290,7 +291,8 @@
             request.start();
             fail("Request should check that the CronetEngine is not shutdown before starting.");
         } catch (IllegalStateException e) {
-            assertEquals("This request's CronetEngine is already shutdown.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "This request's CronetEngine is already shutdown.");
         }
     }
 
@@ -305,11 +307,11 @@
         callback.setAutoAdvance(false);
         request.start();
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
 
         request.cancel();
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_CANCELED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_CANCELED);
         assertThat(callback.mResponseAsString).isEmpty();
     }
 
@@ -332,7 +334,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(responseText.length(), callback.mResponseInfo.getReceivedByteCount());
+        assertThat(callback.mResponseInfo.getReceivedByteCount()).isEqualTo(responseText.length());
     }
 
     @Test
@@ -393,7 +395,7 @@
         callback.blockForDone();
 
         // Verify response from redirected URL is returned.
-        assertEquals(TestUrlRequestCallback.ResponseStep.ON_FAILED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(TestUrlRequestCallback.ResponseStep.ON_FAILED);
     }
 
     @Test
@@ -419,14 +421,14 @@
                         .build();
         request.start();
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
 
         // Asserts that read happens bufferStringLengthMultiplier times
         assertReadCalled(bufferStringLengthMultiplier, callback, request);
 
         callback.startNextRead(request);
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
         assertTrue(Objects.equals(callback.mResponseAsString, longResponseString));
     }
 
@@ -475,7 +477,7 @@
 
         request.start();
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RECEIVED_REDIRECT, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT);
         checkStatus(request, Status.IDLE);
         callback.setAutoAdvance(true);
         request.followRedirect();
@@ -506,7 +508,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
         assertTrue(request.isDone());
     }
 
@@ -531,7 +533,7 @@
                     UploadDataProviders.create(body.getBytes()), callback.getExecutor());
             fail("UploadDataProvider cannot be changed after request has started");
         } catch (IllegalStateException e) {
-            assertEquals("Request is already started. State is: 7", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Request is already started. State is: 7");
         }
     }
 
@@ -549,7 +551,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedUrlChain, callback.mResponseInfo.getUrlChain());
+        assertThat(callback.mResponseInfo.getUrlChain()).isEqualTo(expectedUrlChain);
     }
 
     @Test
@@ -569,7 +571,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedUrlChain, callback.mResponseInfo.getUrlChain());
+        assertThat(callback.mResponseInfo.getUrlChain()).isEqualTo(expectedUrlChain);
     }
 
     @Test
@@ -588,7 +590,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedResponseCode, callback.mResponseInfo.getHttpStatusCode());
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(expectedResponseCode);
     }
 
     @Test
@@ -608,7 +610,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedResponseText, callback.mResponseInfo.getHttpStatusText());
+        assertThat(callback.mResponseInfo.getHttpStatusText()).isEqualTo(expectedResponseText);
     }
 
     @Test
@@ -626,7 +628,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedWasCached, callback.mResponseInfo.wasCached());
+        assertThat(callback.mResponseInfo.wasCached()).isEqualTo(expectedWasCached);
     }
 
     @Test
@@ -647,7 +649,8 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedNegotiatedProtocol, callback.mResponseInfo.getNegotiatedProtocol());
+        assertThat(callback.mResponseInfo.getNegotiatedProtocol())
+                .isEqualTo(expectedNegotiatedProtocol);
     }
 
     @Test
@@ -665,7 +668,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(expectedProxyServer, callback.mResponseInfo.getProxyServer());
+        assertThat(callback.mResponseInfo.getProxyServer()).isEqualTo(expectedProxyServer);
     }
 
     @Test
@@ -686,7 +689,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED);
         // Checks that the exception from {@link DirectPreventingExecutor} was successfully returned
         // to the callback in the onFailed method.
         assertThat(callback.mError)
@@ -718,15 +721,15 @@
                         .build();
         request.start();
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
 
         // Asserts that read happens responseLength times
         assertReadCalled(responseLength, callback, request);
 
         callback.startNextRead(request);
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
-        assertEquals(longResponseString, callback.mResponseAsString);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
+        assertThat(callback.mResponseAsString).isEqualTo(longResponseString);
     }
 
     @Test
@@ -759,15 +762,15 @@
                                          .build();
         request.start();
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
 
         // Asserts that read happens buffer length x multiplier times
         assertReadCalled(responseLength, callback, request);
 
         callback.startNextRead(request);
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
-        assertEquals(longResponseString, callback.mResponseAsString);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
+        assertThat(callback.mResponseAsString).isEqualTo(longResponseString);
     }
 
     @Test
@@ -786,7 +789,8 @@
             request.read(buffer);
             fail("Double read() should be disallowed.");
         } catch (IllegalStateException e) {
-            assertEquals("Invalid state transition - expected 4 but was 7", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "Invalid state transition - expected 4 but was 7");
         }
     }
 
@@ -806,10 +810,11 @@
             callback.startNextRead(request);
             fail("Read should be disallowed while waiting for redirect.");
         } catch (IllegalStateException e) {
-            assertEquals("Invalid state transition - expected 4 but was 3", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "Invalid state transition - expected 4 but was 3");
         }
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RECEIVED_REDIRECT, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT);
         callback.setAutoAdvance(true);
         request.followRedirect();
         callback.blockForDone();
@@ -830,10 +835,10 @@
             mFakeCronetEngine.shutdown();
             fail("Shutdown not checked for active requests.");
         } catch (IllegalStateException e) {
-            assertEquals("Cannot shutdown with running requests.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Cannot shutdown with running requests.");
         }
         callback.waitForNextStep();
-        assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep);
+        assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
         callback.setAutoAdvance(true);
         callback.startNextRead(request);
         callback.blockForDone();
@@ -853,7 +858,7 @@
         request.start();
         callback.blockForDone();
 
-        assertEquals(404, callback.mResponseInfo.getHttpStatusCode());
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(404);
     }
 
     @Test
@@ -871,7 +876,7 @@
             builder.setUploadDataProvider(null, callback.getExecutor());
             fail("Exception not thrown");
         } catch (NullPointerException e) {
-            assertEquals("Invalid UploadDataProvider.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Invalid UploadDataProvider.");
         }
     }
 
@@ -893,7 +898,8 @@
             builder.build().start();
             fail("Exception not thrown");
         } catch (IllegalArgumentException e) {
-            assertEquals("Requests with upload data must have a Content-Type.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "Requests with upload data must have a Content-Type.");
         }
     }
 
@@ -914,8 +920,8 @@
         callback.blockForDone();
 
         assertThat(callback.mResponseInfo).isNotNull();
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEmpty();
         dataProvider.assertClosed();
     }
 
@@ -939,12 +945,12 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(4, dataProvider.getUploadedLength());
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getUploadedLength()).isEqualTo(4);
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("test", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("test");
     }
 
     @Test
@@ -975,8 +981,8 @@
             }
             fail("Cannot read before upload has started");
         } catch (IllegalStateException e) {
-            assertEquals("onReadSucceeded() called when not awaiting a read result; in state: 2",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "onReadSucceeded() called when not awaiting a read result; in state: 2");
         }
         request.cancel();
     }
@@ -1009,8 +1015,8 @@
             }
             fail("Cannot rewind before upload has started");
         } catch (IllegalStateException e) {
-            assertEquals("onRewindSucceeded() called when not awaiting a rewind; in state: 2",
-                    e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo(
+                    "onRewindSucceeded() called when not awaiting a rewind; in state: 2");
         }
         request.cancel();
     }
@@ -1037,12 +1043,12 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(16, dataProvider.getUploadedLength());
-        assertEquals(4, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getUploadedLength()).isEqualTo(16);
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(4);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("Yet another test", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("Yet another test");
     }
 
     @Test
@@ -1067,12 +1073,12 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(16, dataProvider.getUploadedLength());
-        assertEquals(4, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getUploadedLength()).isEqualTo(16);
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(4);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("Yet another test", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("Yet another test");
     }
 
     @Test
@@ -1099,8 +1105,8 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("POST", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("POST");
     }
 
     @Test
@@ -1130,8 +1136,8 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("PUT", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("PUT");
     }
 
     @Test
@@ -1156,11 +1162,11 @@
         dataProvider.assertClosed();
 
         // 1 read call before the rewind, 1 after.
-        assertEquals(2, dataProvider.getNumReadCalls());
-        assertEquals(1, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(2);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("test", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("test");
     }
 
     @Test
@@ -1185,11 +1191,11 @@
         dataProvider.assertClosed();
 
         // 1 read call before the rewind, 1 after.
-        assertEquals(2, dataProvider.getNumReadCalls());
-        assertEquals(1, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(2);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("test", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("test");
     }
 
     @Test
@@ -1230,7 +1236,7 @@
                 .hasCauseThat()
                 .hasMessageThat()
                 .contains("Read upload data length 2 exceeds expected length 1");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1269,7 +1275,7 @@
                 .hasCauseThat()
                 .hasMessageThat()
                 .contains("Read upload data length 8192 exceeds expected length 8191");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1294,14 +1300,14 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(0, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(0);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
         assertThat(callback.mError)
                 .hasMessageThat()
                 .contains("Exception received from UploadDataProvider");
         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync length failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1327,14 +1333,14 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
         assertThat(callback.mError)
                 .hasMessageThat()
                 .contains("Exception received from UploadDataProvider");
         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync read failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1360,14 +1366,14 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
         assertThat(callback.mError)
                 .hasMessageThat()
                 .contains("Exception received from UploadDataProvider");
         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Async read failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1392,14 +1398,14 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
         assertThat(callback.mError)
                 .hasMessageThat()
                 .contains("Exception received from UploadDataProvider");
         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Thrown read failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     /** This test uses a direct executor for upload, and non direct for callbacks */
@@ -1429,8 +1435,8 @@
         builder.build().start();
         callback.blockForDone();
 
-        assertEquals(0, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(0);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
 
         assertThat(callback.mError)
                 .hasMessageThat()
@@ -1439,7 +1445,7 @@
                 .hasCauseThat()
                 .hasMessageThat()
                 .contains("Inline execution is prohibited for this request");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     /** This test uses a direct executor for callbacks, and non direct for upload */
@@ -1470,14 +1476,14 @@
         builder.addHeader("Content-Type", "useless/string");
         builder.build().start();
         callback.blockForDone();
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(0, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
         assertThat(callback.mError).hasMessageThat().contains("Exception posting task to executor");
         assertThat(callback.mError)
                 .hasCauseThat()
                 .hasMessageThat()
                 .contains("Inline execution is prohibited for this request");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
         dataProvider.assertClosed();
     }
 
@@ -1508,8 +1514,8 @@
             throw callback.mError;
         }
 
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("test", callback.mResponseAsString);
+        assertThat(callback.mResponseInfo.getHttpStatusCode()).isEqualTo(200);
+        assertThat(callback.mResponseAsString).isEqualTo("test");
     }
 
     @Test
@@ -1534,14 +1540,14 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(1, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
 
         assertThat(callback.mError)
                 .hasMessageThat()
                 .contains("Exception received from UploadDataProvider");
         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync rewind failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1566,8 +1572,8 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(1, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
 
         assertThat(callback.mError)
                 .hasMessageThat()
@@ -1576,7 +1582,7 @@
                 .hasCauseThat()
                 .hasMessageThat()
                 .contains("Async rewind failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1601,8 +1607,8 @@
         callback.blockForDone();
         dataProvider.assertClosed();
 
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals(1, dataProvider.getNumRewindCalls());
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
 
         assertThat(callback.mError)
                 .hasMessageThat()
@@ -1611,7 +1617,7 @@
                 .hasCauseThat()
                 .hasMessageThat()
                 .contains("Thrown rewind failure");
-        assertEquals(null, callback.mResponseInfo);
+        assertThat(callback.mResponseInfo).isNull();
     }
 
     @Test
@@ -1631,15 +1637,15 @@
         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
         builder.addHeader("Content-Type", "useless/string");
 
-        assertEquals(-1, dataProvider.getUploadedLength());
+        assertThat(dataProvider.getUploadedLength()).isEqualTo(-1);
 
         builder.build().start();
         callback.blockForDone();
         dataProvider.assertClosed();
 
         // 1 read call for one data chunk.
-        assertEquals(1, dataProvider.getNumReadCalls());
-        assertEquals("test hello", callback.mResponseAsString);
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
+        assertThat(callback.mResponseAsString).isEqualTo("test hello");
     }
 
     @Test
@@ -1662,15 +1668,15 @@
         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
         builder.addHeader("Content-Type", "useless/string");
 
-        assertEquals(-1, dataProvider.getUploadedLength());
+        assertThat(dataProvider.getUploadedLength()).isEqualTo(-1);
 
         builder.build().start();
         callback.blockForDone();
         dataProvider.assertClosed();
 
         // 2 read call for the first two data chunks, and 1 for final chunk.
-        assertEquals(3, dataProvider.getNumReadCalls());
-        assertEquals("hello there!", callback.mResponseAsString);
+        assertThat(dataProvider.getNumReadCalls()).isEqualTo(3);
+        assertThat(callback.mResponseAsString).isEqualTo("hello there!");
     }
 
     /**
diff --git a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlResponseTest.java b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlResponseTest.java
index 4eea478d..ec03339 100644
--- a/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlResponseTest.java
+++ b/components/cronet/android/fake/javatests/org/chromium/net/test/FakeUrlResponseTest.java
@@ -6,10 +6,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -23,7 +19,6 @@
 import java.io.UnsupportedEncodingException;
 import java.util.AbstractMap;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -80,9 +75,8 @@
         FakeUrlResponse responseNotEqualToTestResponse =
                 mTestResponse.toBuilder().setResponseBody("Not equal".getBytes()).build();
 
-        assertEquals(mTestResponse, mTestResponse);
-        assertEquals(mTestResponse, responseEqualToTestResponse);
-        assertNotEquals(mTestResponse, responseNotEqualToTestResponse);
+        assertThat(mTestResponse).isEqualTo(responseEqualToTestResponse);
+        assertThat(mTestResponse).isNotEqualTo(responseNotEqualToTestResponse);
     }
 
     @Test
@@ -91,8 +85,8 @@
         try {
             FakeUrlResponse responseWithBodySetAsBytes =
                     mTestResponse.toBuilder().setResponseBody(TEST_BODY.getBytes("UTF-8")).build();
-            assertTrue(Arrays.equals(
-                    mTestResponse.getResponseBody(), responseWithBodySetAsBytes.getResponseBody()));
+            assertThat(mTestResponse.getResponseBody())
+                    .isEqualTo(responseWithBodySetAsBytes.getResponseBody());
         } catch (UnsupportedEncodingException e) {
             throw new RuntimeException(
                     "Exception occurred while encoding response body: " + TEST_BODY);
@@ -110,7 +104,7 @@
         List<Map.Entry<String, String>> responseHeadersListWithHeader =
                 responseWithHeader.getAllHeadersList();
 
-        assertNotEquals(responseHeadersListWithHeader, responseHeadersList);
+        assertThat(responseHeadersList).isNotEqualTo(responseHeadersListWithHeader);
     }
 
     @Test
@@ -135,8 +129,8 @@
     public void testHashCodeReturnsSameIntForEqualObjects() {
         FakeUrlResponse responseEqualToTest = mTestResponse.toBuilder().build();
 
-        assertEquals(mTestResponse.hashCode(), mTestResponse.hashCode());
-        assertEquals(mTestResponse.hashCode(), responseEqualToTest.hashCode());
+        assertThat(mTestResponse.hashCode()).isEqualTo(mTestResponse.hashCode());
+        assertThat(mTestResponse.hashCode()).isEqualTo(responseEqualToTest.hashCode());
         // Two non-equivalent values can map to the same hashCode.
     }
 
@@ -149,7 +143,7 @@
                 + " Proxy Server: " + TEST_PROXY_SERVER + " Response Body (UTF-8): " + TEST_BODY;
         String responseToString = mTestResponse.toString();
 
-        assertEquals(expectedString, responseToString);
+        assertThat(responseToString).isEqualTo(expectedString);
     }
 
     @Test
@@ -167,7 +161,7 @@
 
         FakeUrlResponse constructedResponse = new FakeUrlResponse(info);
 
-        assertEquals(expectedResponse, constructedResponse);
+        assertThat(constructedResponse).isEqualTo(expectedResponse);
     }
 
     @Test
@@ -187,7 +181,7 @@
 
         FakeUrlResponse constructedResponse = new FakeUrlResponse(info);
 
-        assertEquals(expectedResponse, constructedResponse);
+        assertThat(constructedResponse).isEqualTo(expectedResponse);
     }
 
     @Test
@@ -199,8 +193,8 @@
                         .build();
         FakeUrlResponse defaultResponse = new FakeUrlResponse.Builder().build();
 
-        assertNotEquals(
-                defaultResponse.getAllHeadersList(), defaultResponseWithHeader.getAllHeadersList());
+        assertThat(defaultResponseWithHeader.getAllHeadersList())
+                .isNotEqualTo(defaultResponse.getAllHeadersList());
     }
 
     @Test
diff --git a/components/cronet/android/fake/javatests/org/chromium/net/test/UrlResponseMatcherTest.java b/components/cronet/android/fake/javatests/org/chromium/net/test/UrlResponseMatcherTest.java
index 38396f79..3613565 100644
--- a/components/cronet/android/fake/javatests/org/chromium/net/test/UrlResponseMatcherTest.java
+++ b/components/cronet/android/fake/javatests/org/chromium/net/test/UrlResponseMatcherTest.java
@@ -6,7 +6,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -28,7 +27,7 @@
                     new UrlResponseMatcher(null, new FakeUrlResponse.Builder().build());
             fail("URL not null-checked");
         } catch (NullPointerException e) {
-            assertEquals("URL is required.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("URL is required.");
         }
     }
 
@@ -39,7 +38,7 @@
             UrlResponseMatcher matcher = new UrlResponseMatcher("url", null);
             fail("Response not null-checked");
         } catch (NullPointerException e) {
-            assertEquals("Response is required.", e.getMessage());
+            assertThat(e).hasMessageThat().isEqualTo("Response is required.");
         }
     }
 
@@ -53,8 +52,7 @@
 
         FakeUrlResponse found = matcher.getMatchingResponse(url, null, null, null);
 
-        assertThat(found).isNotNull();
-        assertEquals(found, response);
+        assertThat(found).isEqualTo(response);
     }
 
     @Test
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/impl/CronetLoggerTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/impl/CronetLoggerTest.java
index 3803b7ca..bcad7c9 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/impl/CronetLoggerTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/impl/CronetLoggerTest.java
@@ -10,7 +10,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP;
@@ -420,12 +419,12 @@
 
         final CronetTrafficInfo trafficInfo = mTestLogger.getLastCronetTrafficInfo();
         assertThat(trafficInfo.getRequestHeaderSizeInBytes()).isEqualTo(0);
-        assertNotEquals(0, trafficInfo.getRequestBodySizeInBytes());
-        assertNotEquals(0, trafficInfo.getResponseHeaderSizeInBytes());
-        assertNotEquals(0, trafficInfo.getResponseBodySizeInBytes());
+        assertThat(trafficInfo.getRequestBodySizeInBytes()).isNotEqualTo(0);
+        assertThat(trafficInfo.getResponseHeaderSizeInBytes()).isNotEqualTo(0);
+        assertThat(trafficInfo.getResponseBodySizeInBytes()).isNotEqualTo(0);
         assertThat(trafficInfo.getResponseStatusCode()).isEqualTo(200);
-        assertNotEquals(Duration.ofSeconds(0), trafficInfo.getHeadersLatency());
-        assertNotEquals(Duration.ofSeconds(0), trafficInfo.getTotalLatency());
+        assertThat(trafficInfo.getHeadersLatency()).isNotEqualTo(Duration.ofSeconds(0));
+        assertThat(trafficInfo.getTotalLatency()).isNotEqualTo(Duration.ofSeconds(0));
         assertThat(trafficInfo.getNegotiatedProtocol()).isNotNull();
         assertFalse(trafficInfo.wasConnectionMigrationAttempted());
         assertFalse(trafficInfo.didConnectionMigrationSucceed());
diff --git a/components/device_signals/core/browser/ash/BUILD.gn b/components/device_signals/core/browser/ash/BUILD.gn
index 51f6a922..4330b9a 100644
--- a/components/device_signals/core/browser/ash/BUILD.gn
+++ b/components/device_signals/core/browser/ash/BUILD.gn
@@ -12,6 +12,8 @@
   sources = [ "user_permission_service_ash.cc" ]
 
   public_deps = [ "//components/device_signals/core/browser" ]
+
+  deps = [ "//ash/constants" ]
 }
 
 source_set("unit_tests") {
@@ -20,6 +22,7 @@
 
   deps = [
     ":ash",
+    "//ash/constants",
     "//base/test:test_support",
     "//components/device_signals/core/browser",
     "//components/device_signals/core/browser:test_support",
diff --git a/components/device_signals/core/browser/ash/DEPS b/components/device_signals/core/browser/ash/DEPS
new file mode 100644
index 0000000..f514004
--- /dev/null
+++ b/components/device_signals/core/browser/ash/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ash/constants",
+]
diff --git a/components/device_signals/core/browser/ash/user_permission_service_ash.cc b/components/device_signals/core/browser/ash/user_permission_service_ash.cc
index dbec67e..9780f64 100644
--- a/components/device_signals/core/browser/ash/user_permission_service_ash.cc
+++ b/components/device_signals/core/browser/ash/user_permission_service_ash.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "components/device_signals/core/browser/user_delegate.h"
 
 namespace device_signals {
@@ -31,6 +32,11 @@
     return UserPermission::kGranted;
   }
 
+  if (ash::features::IsUnmanagedDeviceDeviceTrustConnectorFeatureEnabled() &&
+      !IsDeviceCloudManaged() && user_delegate_->IsManagedUser()) {
+    return UserPermission::kGranted;
+  }
+
   // Other use-cases are currently unsupported. Further scenarios breakdown will
   // be added in the future.
   return UserPermission::kUnsupported;
diff --git a/components/device_signals/core/browser/ash/user_permission_service_ash_unittest.cc b/components/device_signals/core/browser/ash/user_permission_service_ash_unittest.cc
index 8042b39..19dc327 100644
--- a/components/device_signals/core/browser/ash/user_permission_service_ash_unittest.cc
+++ b/components/device_signals/core/browser/ash/user_permission_service_ash_unittest.cc
@@ -4,10 +4,13 @@
 
 #include "components/device_signals/core/browser/ash/user_permission_service_ash.h"
 
+#include "ash/constants/ash_features.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/device_signals/core/browser/mock_user_delegate.h"
 #include "components/device_signals/core/browser/pref_names.h"
 #include "components/device_signals/core/browser/user_delegate.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
 #include "components/policy/core/common/management/management_service.h"
 #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
 #include "components/prefs/pref_registry.h"
@@ -41,7 +44,9 @@
 
 class UserPermissionServiceAshTest : public testing::Test {
  protected:
-  UserPermissionServiceAshTest() {
+  explicit UserPermissionServiceAshTest(
+      bool feature_unmanaged_device_enabled = true)
+      : feature_unmanaged_device_enabled_(feature_unmanaged_device_enabled) {
     RegisterProfilePrefs(test_prefs_.registry());
 
     auto mock_user_delegate =
@@ -52,14 +57,31 @@
         &management_service_, std::move(mock_user_delegate), &test_prefs_);
   }
 
-  void SetDeviceAsCloudManaged() {
-    scoped_override_.emplace(&management_service_,
-                             EnterpriseManagementAuthority::CLOUD_DOMAIN);
+  void SetUp() override {
+    feature_list_.InitWithFeatureState(
+        ash::features::kUnmanagedDeviceDeviceTrustConnectorEnabled,
+        feature_unmanaged_device_enabled_);
   }
 
-  void SetUserAsCloudManaged() {
-    scoped_override_.emplace(&management_service_,
-                             EnterpriseManagementAuthority::CLOUD);
+  void SetDeviceAsCloudManaged(bool is_managed = true) {
+    if (is_managed) {
+      scoped_override_.emplace(&management_service_,
+                               EnterpriseManagementAuthority::CLOUD_DOMAIN);
+    }
+
+    EXPECT_EQ(management_service_.HasManagementAuthority(
+                  EnterpriseManagementAuthority::CLOUD_DOMAIN),
+              is_managed);
+  }
+
+  void SetUserAsCloudManaged(bool is_managed = true) {
+    if (is_managed) {
+      scoped_override_.emplace(&management_service_,
+                               EnterpriseManagementAuthority::CLOUD);
+    }
+
+    EXPECT_CALL(*mock_user_delegate_, IsManagedUser())
+        .WillRepeatedly(Return(is_managed));
   }
 
   void SetSigninContext(bool is_signin_context = true) {
@@ -78,8 +100,11 @@
   absl::optional<ScopedManagementServiceOverrideForTesting> scoped_override_;
   raw_ptr<testing::StrictMock<MockUserDelegate>> mock_user_delegate_;
   TestingPrefServiceSimple test_prefs_;
+  base::test::ScopedFeatureList feature_list_;
 
   std::unique_ptr<UserPermissionServiceAsh> permission_service_;
+
+  const bool feature_unmanaged_device_enabled_;
 };
 
 // Tests that ShouldCollectConsent should always return false on CrOS, as the
@@ -120,12 +145,46 @@
             UserPermission::kUnsupported);
 }
 
-// Tests that signals cannot be collected if the device is unmanaged.
-TEST_F(UserPermissionServiceAshTest, CanCollectSignals_UnmanagedDevice) {
-  SetUserAsCloudManaged();
+// Tests that signals cannot be collected on the signin screen if the device is
+// unmanaged.
+TEST_F(UserPermissionServiceAshTest,
+       CanCollectSignals_UnmanagedDevice_UnmanagedUser) {
+  SetDeviceAsCloudManaged(/*is_managed=*/false);
+  SetUserAsCloudManaged(/*is_managed=*/false);
 
   EXPECT_EQ(permission_service_->CanCollectSignals(),
             UserPermission::kUnsupported);
 }
 
+// Tests that signals can be collected if the device is unmanaged and the user
+// is managed.
+TEST_F(UserPermissionServiceAshTest,
+       CanCollectSignals_UnmanagedDevice_ManagedUser) {
+  SetDeviceAsCloudManaged(/*is_managed=*/false);
+  SetUserAsCloudManaged();
+
+  EXPECT_EQ(permission_service_->CanCollectSignals(), UserPermission::kGranted);
+}
+
+class UserPermissionServiceAshFlagTest
+    : public UserPermissionServiceAshTest,
+      public ::testing::WithParamInterface<bool> {
+ protected:
+  UserPermissionServiceAshFlagTest()
+      : UserPermissionServiceAshTest(GetParam()) {}
+};
+
+TEST_P(UserPermissionServiceAshFlagTest, CanCollectSignals_UnmanagedDevice) {
+  SetDeviceAsCloudManaged(/*is_managed=*/false);
+  SetUserAsCloudManaged();
+
+  EXPECT_EQ(
+      permission_service_->CanCollectSignals(),
+      GetParam() ? UserPermission::kGranted : UserPermission::kUnsupported);
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         UserPermissionServiceAshFlagTest,
+                         testing::Bool());
+
 }  // namespace device_signals
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 15f1932..81f05a3 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -193,7 +193,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
 void ContentPasswordManagerDriver::KeyboardReplacingSurfaceClosed(
-    ToShowVirtualKeyboard show_virtual_keyboard) {
+    ShowVirtualKeyboard show_virtual_keyboard) {
   GetPasswordAutofillAgent()->KeyboardReplacingSurfaceClosed(
       show_virtual_keyboard.value());
 }
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h
index baf2ae8..7da0fcb 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -75,7 +75,7 @@
                             const std::u16string& credential) override;
 #if BUILDFLAG(IS_ANDROID)
   void KeyboardReplacingSurfaceClosed(
-      ToShowVirtualKeyboard show_virtual_keyboard) override;
+      ShowVirtualKeyboard show_virtual_keyboard) override;
   void TriggerFormSubmission() override;
 #endif
   void PreviewSuggestion(const std::u16string& username,
diff --git a/components/password_manager/core/browser/password_manager_driver.h b/components/password_manager/core/browser/password_manager_driver.h
index c499610c..a3e65ae 100644
--- a/components/password_manager/core/browser/password_manager_driver.h
+++ b/components/password_manager/core/browser/password_manager_driver.h
@@ -36,8 +36,8 @@
     : public base::SupportsWeakPtr<PasswordManagerDriver> {
  public:
 #if BUILDFLAG(IS_ANDROID)
-  using ToShowVirtualKeyboard =
-      base::StrongAlias<class ToShowVirtualKeyboardTag, bool>;
+  using ShowVirtualKeyboard =
+      base::StrongAlias<class ShowVirtualKeyboardTag, bool>;
 #endif
 
   PasswordManagerDriver() = default;
@@ -96,7 +96,7 @@
   // Fill sheet) has been closed. Indicates whether the virtual keyboard should
   // be shown instead.
   virtual void KeyboardReplacingSurfaceClosed(
-      ToShowVirtualKeyboard show_virtual_keyboard) {}
+      ShowVirtualKeyboard show_virtual_keyboard) {}
 
   // Triggers form submission on the last interacted web input element.
   virtual void TriggerFormSubmission() {}
diff --git a/components/permissions/permission_manager_unittest.cc b/components/permissions/permission_manager_unittest.cc
index f68b13b..6f2122c 100644
--- a/components/permissions/permission_manager_unittest.cc
+++ b/components/permissions/permission_manager_unittest.cc
@@ -253,7 +253,7 @@
     std::vector<blink::OriginWithPossibleWildcards> parsed_origins;
     for (const std::string& origin : origins)
       parsed_origins.emplace_back(
-          blink::OriginWithPossibleWildcards::FromOrigin(
+          *blink::OriginWithPossibleWildcards::FromOrigin(
               url::Origin::Create(GURL(origin))));
     navigation->SetPermissionsPolicyHeader(
         {{feature, parsed_origins, /*self_if_matches=*/absl::nullopt,
@@ -271,8 +271,8 @@
     if (feature != PermissionsPolicyFeature::kNotFound) {
       frame_policy.emplace_back(
           feature,
-          std::vector({blink::OriginWithPossibleWildcards::FromOrigin(
-              url::Origin::Create(origin))}),
+          std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(
+              url::Origin::Create(origin))},
           /*self_if_matches=*/absl::nullopt,
           /*matches_all_origins=*/false,
           /*matches_opaque_src=*/false);
diff --git a/components/permissions/permission_uma_util_unittest.cc b/components/permissions/permission_uma_util_unittest.cc
index 9dd8882..df9deea7 100644
--- a/components/permissions/permission_uma_util_unittest.cc
+++ b/components/permissions/permission_uma_util_unittest.cc
@@ -53,7 +53,7 @@
     bool matches_all_origins = false) {
   std::vector<blink::OriginWithPossibleWildcards> allow_origins;
   for (const auto& origin : origins) {
-    allow_origins.emplace_back(blink::OriginWithPossibleWildcards::FromOrigin(
+    allow_origins.emplace_back(*blink::OriginWithPossibleWildcards::FromOrigin(
         url::Origin::Create(GURL(origin))));
   }
   return {{feature, allow_origins, /*self_if_matches=*/absl::nullopt,
diff --git a/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml b/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml
index b45ac79..b3daab55 100644
--- a/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml
+++ b/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml
@@ -14,7 +14,7 @@
 
         The source code of any extension may be altered by users through developer tools, potentially rendering the extension dysfunctional. If this is a concern, set the <ph name="DEVELOPER_TOOLS_DISABLED_POLICY_NAME">DeveloperToolsDisabled</ph> policy.
 
-        Each list item of the policy is a string that contains an extension ID and, optionally, an update URL separated by a semicolon (;). The extension ID is the 32-letter string found, for example, on chrome://extensions when in Developer mode. If specified, the update URL should point to an Update Manifest XML document ( https://developer.chrome.com/extensions/autoupdate ). The update URL should use one of the following schemes: <ph name="HTTP_SCHEME">http</ph>, <ph name="HTTPS_SCHEME">https</ph> or <ph name="FILE_SCHEME">file</ph>. By default, the Chrome Web Store's update URL is used. The update URL set in this policy is only used for the initial installation; subsequent updates of the extension use the update URL in the extension's manifest. The update url for subsequent updates can be overriden using the <ph name="EXTENSION_SETTINGS_POLICY_NAME">ExtensionSettings</ph> policy, see http://support.google.com/chrome/a?p=Configure_ExtensionSettings_policy.
+        Each list item of the policy is a string that contains an extension ID and, optionally, an update URL separated by a semicolon (;). The extension ID is the 32-letter string found, for example, on chrome://extensions when in Developer mode. If specified, the update URL should point to an Update Manifest XML document ( https://developer.chrome.com/extensions/autoupdate ). The update URL should use one of the following schemes: <ph name="HTTP_SCHEME">http</ph>, <ph name="HTTPS_SCHEME">https</ph> or <ph name="FILE_SCHEME">file</ph>. By default, the Chrome Web Store's update URL is used. The update URL set in this policy is only used for the initial installation; subsequent updates of the extension use the update URL in the extension's manifest. The update url for subsequent updates can be overridden using the <ph name="EXTENSION_SETTINGS_POLICY_NAME">ExtensionSettings</ph> policy, see http://support.google.com/chrome/a?p=Configure_ExtensionSettings_policy.
 
          Note: This policy doesn't apply to Incognito mode. Read about hosting extensions ( https://developer.chrome.com/extensions/hosting ).
 example_value:
diff --git a/components/signin/public/identity_manager/access_token_constants.cc b/components/signin/public/identity_manager/access_token_constants.cc
index 2bc4d04..b39b98c 100644
--- a/components/signin/public/identity_manager/access_token_constants.cc
+++ b/components/signin/public/identity_manager/access_token_constants.cc
@@ -86,6 +86,7 @@
       GaiaConstants::kCloudTranslationOAuth2Scope,
       GaiaConstants::kDriveOAuth2Scope,
       GaiaConstants::kDriveReadOnlyOAuth2Scope,
+      GaiaConstants::kExperimentsAndConfigsOAuth2Scope,
       GaiaConstants::kGCMGroupServerOAuth2Scope,
       GaiaConstants::kCloudPlatformProjectsOAuth2Scope,
       GaiaConstants::kNearbyShareOAuth2Scope,
diff --git a/components/sync/engine/get_updates_processor.cc b/components/sync/engine/get_updates_processor.cc
index 6febc99..5b513b0a 100644
--- a/components/sync/engine/get_updates_processor.cc
+++ b/components/sync/engine/get_updates_processor.cc
@@ -162,10 +162,6 @@
   // (e.g. Bookmark URLs but not their containing folders).
   get_updates->set_fetch_folders(true);
 
-  // This is a deprecated field that should be cleaned up after server's
-  // behavior is updated.
-  get_updates->set_create_mobile_bookmarks_folder(true);
-
   bool need_encryption_key = ShouldRequestEncryptionKey(cycle->context());
   get_updates->set_need_encryption_key(need_encryption_key);
 
diff --git a/components/sync/nigori/nigori_state.cc b/components/sync/nigori/nigori_state.cc
index 73f8d66..ea05d5c 100644
--- a/components/sync/nigori/nigori_state.cc
+++ b/components/sync/nigori/nigori_state.cc
@@ -11,6 +11,7 @@
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/nigori/key_derivation_params.h"
+#include "components/sync/engine/nigori/public_key.h"
 #include "components/sync/engine/sync_encryption_handler.h"
 #include "components/sync/nigori/cryptographer_impl.h"
 #include "components/sync/nigori/keystore_keys_cryptographer.h"
@@ -103,6 +104,22 @@
   }
 }
 
+absl::optional<PublicKey> PublicKeyFromProto(
+    const sync_pb::PublicKey& public_key) {
+  std::vector<uint8_t> key(public_key.x25519_public_key().begin(),
+                           public_key.x25519_public_key().end());
+  return PublicKey::CreateByImport(key);
+}
+
+sync_pb::PublicKey PublicKeyToProto(const PublicKey& public_key,
+                                    uint32_t key_pair_version) {
+  sync_pb::PublicKey output;
+  const auto key = public_key.GetRawPublicKey();
+  output.set_x25519_public_key(std::string(key.begin(), key.end()));
+  output.set_version(key_pair_version);
+  return output;
+}
+
 }  // namespace
 
 // static
@@ -154,6 +171,10 @@
 
   state.trusted_vault_debug_info = proto.trusted_vault_debug_info();
 
+  if (proto.has_public_key()) {
+    state.public_key = PublicKeyFromProto(proto.public_key());
+    state.key_pair_version = proto.public_key().version();
+  }
   return state;
 }
 
@@ -217,6 +238,10 @@
         *last_default_trusted_vault_key_name);
   }
   *proto.mutable_trusted_vault_debug_info() = trusted_vault_debug_info;
+  if (public_key.has_value() && key_pair_version.has_value()) {
+    *proto.mutable_public_key() =
+        PublicKeyToProto(public_key.value(), key_pair_version.value());
+  }
   return proto;
 }
 
@@ -266,6 +291,12 @@
         TimeToProtoTime(custom_passphrase_time));
   }
   *specifics.mutable_trusted_vault_debug_info() = trusted_vault_debug_info;
+
+  if (public_key.has_value() && key_pair_version.has_value()) {
+    *specifics.mutable_public_key() =
+        PublicKeyToProto(public_key.value(), key_pair_version.value());
+  }
+
   return specifics;
 }
 
@@ -284,6 +315,10 @@
   result.last_default_trusted_vault_key_name =
       last_default_trusted_vault_key_name;
   result.trusted_vault_debug_info = trusted_vault_debug_info;
+  if (public_key.has_value()) {
+    result.public_key = public_key->Clone();
+  }
+  result.key_pair_version = key_pair_version;
   return result;
 }
 
diff --git a/components/sync/nigori/nigori_state.h b/components/sync/nigori/nigori_state.h
index f6bff05d..ae6a03e 100644
--- a/components/sync/nigori/nigori_state.h
+++ b/components/sync/nigori/nigori_state.h
@@ -97,6 +97,8 @@
 
   // Current Public-key.
   absl::optional<PublicKey> public_key;
+  // Current Public-key version.
+  absl::optional<uint32_t> key_pair_version;
 };
 
 }  // namespace syncer
diff --git a/components/sync/nigori/nigori_state_unittest.cc b/components/sync/nigori/nigori_state_unittest.cc
index 37220dbf..5626457d 100644
--- a/components/sync/nigori/nigori_state_unittest.cc
+++ b/components/sync/nigori/nigori_state_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/sync/nigori/nigori_state.h"
 
+#include <vector>
+
 #include "components/sync/base/time.h"
 #include "components/sync/engine/nigori/key_derivation_params.h"
 #include "components/sync/engine/nigori/nigori.h"
@@ -93,6 +95,55 @@
   EXPECT_THAT(specifics.keystore_migration_time(), Eq(TimeToProtoTime(now)));
 }
 
+TEST(NigoriStateTest, ShouldConvertPublicKeyStateToSpecifics) {
+  const std::string kDefaultEncryptionKey = "defaultkey";
+  NigoriState state;
+  const std::string default_encryption_key_name =
+      state.cryptographer->EmplaceKey(kDefaultEncryptionKey,
+                                      KeyDerivationParams::CreateForPbkdf2());
+  state.cryptographer->SelectDefaultEncryptionKey(default_encryption_key_name);
+  const std::vector<uint8_t> key(32, 0xDE);
+  state.public_key = PublicKey::CreateByImport(key);
+  state.key_pair_version = 1;
+
+  NigoriSpecifics specifics = state.ToSpecificsProto();
+
+  EXPECT_THAT(specifics.public_key().x25519_public_key(),
+              testing::ElementsAreArray(key));
+  EXPECT_THAT(specifics.public_key().version(), Eq(1));
+}
+
+TEST(NigoriStateTest, ShouldContainPublicKeyInLocalProto) {
+  const std::string kDefaultEncryptionKey = "defaultkey";
+  NigoriState state;
+  const std::string default_encryption_key_name =
+      state.cryptographer->EmplaceKey(kDefaultEncryptionKey,
+                                      KeyDerivationParams::CreateForPbkdf2());
+  state.cryptographer->SelectDefaultEncryptionKey(default_encryption_key_name);
+  const std::vector<uint8_t> key(32, 0xDE);
+  state.public_key = PublicKey::CreateByImport(key);
+  state.key_pair_version = 1;
+
+  sync_pb::NigoriModel nigori_model = state.ToLocalProto();
+
+  ASSERT_THAT(nigori_model.public_key().x25519_public_key(),
+              testing::ElementsAreArray(key));
+  ASSERT_THAT(nigori_model.public_key().version(), Eq(1));
+}
+
+TEST(NigoriStateTest, ShouldClonePublicKey) {
+  NigoriState state;
+  const std::vector<uint8_t> key(32, 0xDE);
+  state.public_key = PublicKey::CreateByImport(key);
+  state.key_pair_version = 1;
+
+  NigoriState cloned_state = state.Clone();
+
+  EXPECT_THAT(cloned_state.public_key->GetRawPublicKey(),
+              testing::ElementsAreArray(state.public_key->GetRawPublicKey()));
+  EXPECT_EQ(cloned_state.key_pair_version, state.key_pair_version);
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index b1cb3981..47b330f 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -650,7 +650,6 @@
   VISIT_REP(from_progress_marker);
   VISIT(streaming);
   VISIT(need_encryption_key);
-  VISIT(create_mobile_bookmarks_folder);
   VISIT_ENUM(get_updates_origin);
   VISIT(is_retry);
   VISIT_REP(client_contexts);
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto
index 86d8b92..e093ec5 100644
--- a/components/sync/protocol/sync.proto
+++ b/components/sync/protocol/sync.proto
@@ -186,11 +186,6 @@
   // other datatypes, so repeated usage will likely result in throttling.
   optional bool need_encryption_key = 8 [default = false];
 
-  // Whether to create the mobile bookmarks folder if it's not
-  // already created.  Set to true by all modern clients.
-  optional bool create_mobile_bookmarks_folder = 1000
-      [default = false, deprecated = true];
-
   // This value is an updated version of the GetUpdatesCallerInfo's
   // GetUpdatesSource.  It describes the reason for the GetUpdate request.
   // Introduced in M29.
@@ -209,6 +204,8 @@
   reserved "requested_types";
   reserved 5;
   reserved "batch_size";
+  reserved 1000;
+  reserved "create_mobile_bookmarks_folder";
 }
 
 // Message from a client asking the server to clear its data. This causes the
diff --git a/components/ukm/BUILD.gn b/components/ukm/BUILD.gn
index 507519a..fe6599a 100644
--- a/components/ukm/BUILD.gn
+++ b/components/ukm/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//testing/test.gni")
 
 component("ukm_recorder") {
@@ -97,6 +98,9 @@
     "//google_apis",
     "//services/metrics/public/cpp:metrics_cpp",
   ]
+  if (is_chromeos) {
+    deps += [ "//chromeos/components/kiosk" ]
+  }
 
   public_deps = [ "//components/unified_consent" ]
 }
@@ -148,6 +152,9 @@
     "//third_party/zlib/google:compression_utils",
     "//url",
   ]
+  if (is_chromeos) {
+    deps += [ "//chromeos/components/kiosk:test_support" ]
+  }
 }
 
 # Convenience testing target
diff --git a/components/ukm/DEPS b/components/ukm/DEPS
index a14a9c1..99cc195 100644
--- a/components/ukm/DEPS
+++ b/components/ukm/DEPS
@@ -12,6 +12,7 @@
 
 specific_include_rules = {
   ".*unittest\.cc": [
+    "+chromeos/components/kiosk/kiosk_test_utils.h",
     "+components/sync_preferences/testing_pref_service_syncable.h",
     "+services/metrics/ukm_recorder_interface.h",
     "+services/metrics/ukm_recorder_factory_impl.h",
diff --git a/components/ukm/observers/DEPS b/components/ukm/observers/DEPS
index 158776a8..4c99eac 100644
--- a/components/ukm/observers/DEPS
+++ b/components/ukm/observers/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chromeos/components/kiosk",
   "+components/metrics",
   "+components/history/core/browser",
   "+components/sync",
diff --git a/components/ukm/observers/ukm_consent_state_observer.cc b/components/ukm/observers/ukm_consent_state_observer.cc
index 332a8d6..ed749fcb 100644
--- a/components/ukm/observers/ukm_consent_state_observer.cc
+++ b/components/ukm/observers/ukm_consent_state_observer.cc
@@ -17,13 +17,25 @@
 #include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/components/kiosk/kiosk_utils.h"  // nogncheck
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 using unified_consent::UrlKeyedDataCollectionConsentHelper;
 
 namespace ukm {
 namespace {
 
 bool CanUploadUkmForType(syncer::SyncService* sync_service,
-                         syncer::ModelType model_type) {
+                         syncer::ModelType model_type,
+                         bool msbb_consent) {
+#if BUILDFLAG(IS_CHROMEOS)
+  // Enable uploading of UKM for Kiosk only if MSBB consent is set.
+  if (chromeos::IsKioskSession()) {
+    return msbb_consent;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
   switch (GetUploadToGoogleState(sync_service, model_type)) {
     case syncer::UploadState::NOT_ACTIVE:
       return false;
@@ -72,17 +84,20 @@
   const bool msbb_consent =
       consent_helper->IsEnabled() || metrics::IsMsbbSettingForcedOnForUkm();
 
-  if (msbb_consent)
+  if (msbb_consent) {
     state.SetConsentType(MSBB);
+  }
 
   if (msbb_consent &&
-      CanUploadUkmForType(sync_service, syncer::ModelType::EXTENSIONS)) {
+      CanUploadUkmForType(sync_service, syncer::ModelType::EXTENSIONS,
+                          msbb_consent)) {
     state.SetConsentType(EXTENSIONS);
   }
 
   if ((msbb_consent ||
        base::FeatureList::IsEnabled(kAppMetricsOnlyRelyOnAppSync)) &&
-      CanUploadUkmForType(sync_service, syncer::ModelType::APPS)) {
+      CanUploadUkmForType(sync_service, syncer::ModelType::APPS,
+                          msbb_consent)) {
     state.SetConsentType(APPS);
   }
 
diff --git a/components/ukm/observers/ukm_consent_state_observer_unittest.cc b/components/ukm/observers/ukm_consent_state_observer_unittest.cc
index bbe4f714..67a87eb 100644
--- a/components/ukm/observers/ukm_consent_state_observer_unittest.cc
+++ b/components/ukm/observers/ukm_consent_state_observer_unittest.cc
@@ -14,6 +14,10 @@
 #include "components/unified_consent/unified_consent_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/components/kiosk/kiosk_test_utils.h"  // nogncheck
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 namespace ukm {
 
 namespace {
@@ -51,9 +55,9 @@
 
     NotifyObserversOfStateChanged();
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     SetAppSync(false);
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
   }
 
   void Shutdown() override {
@@ -62,8 +66,9 @@
     }
   }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   void SetAppSync(bool enabled) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     auto selected_os_types = GetUserSettings()->GetSelectedOsTypes();
 
     if (enabled)
@@ -72,10 +77,13 @@
       selected_os_types.Remove(syncer::UserSelectableOsType::kOsApps);
 
     GetUserSettings()->SetSelectedOsTypes(false, selected_os_types);
+#else
+    GetUserSettings()->SetAppsSyncEnabledByOs(enabled);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     NotifyObserversOfStateChanged();
   }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
  private:
   // syncer::TestSyncService:
@@ -128,7 +136,7 @@
   bool notified_ = false;
 };
 
-class UkmConsentStateObserverTest : public testing::Test {
+class UkmConsentStateObserverTest : public testing::TestWithParam<bool> {
  public:
   UkmConsentStateObserverTest() = default;
 
@@ -382,4 +390,43 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS)
+
+// Test consent state for kiosk.
+class KioskUkmConsentStateObserverTest : public UkmConsentStateObserverTest {
+ public:
+  bool is_ukm_collection_enabled() const { return GetParam(); }
+
+  void SetUp() override { chromeos::SetUpFakeKioskSession(); }
+
+  void TearDown() override { chromeos::TearDownFakeKioskSession(); }
+};
+
+TEST_P(KioskUkmConsentStateObserverTest, VerifyDefaultConsent) {
+  sync_preferences::TestingPrefServiceSyncable prefs;
+  RegisterUrlKeyedAnonymizedDataCollectionPref(prefs);
+  TestUkmConsentStateObserver observer;
+  MockSyncService sync;
+  // Disable app sync consent.
+  sync.SetAppSync(false);
+
+  // Enable MSBB consent.
+  SetUrlKeyedAnonymizedDataCollectionEnabled(&prefs,
+                                             is_ukm_collection_enabled());
+  observer.StartObserving(&sync, &prefs);
+
+  UkmConsentState state = observer.GetUkmConsentState();
+
+  EXPECT_EQ(is_ukm_collection_enabled(), observer.IsUkmAllowedForAllProfiles());
+  // MSBB and Extensions are enabled while App Sync is disabled.
+  EXPECT_EQ(is_ukm_collection_enabled(), state.Has(MSBB));
+  EXPECT_EQ(is_ukm_collection_enabled(), state.Has(APPS));
+}
+
+INSTANTIATE_TEST_SUITE_P(KioskUkmConsentStateObserverTest,
+                         KioskUkmConsentStateObserverTest,
+                         ::testing::Bool());
+
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace ukm
diff --git a/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc b/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc
index aeb89670..cd5c240d 100644
--- a/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc
+++ b/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc
@@ -74,6 +74,21 @@
         size, format, usage, gpu::kNullSurfaceHandle, nullptr);
   }
 
+  void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
+                         const SharedImageFormat& si_format,
+                         const gfx::ColorSpace& color_space,
+                         GrSurfaceOrigin surface_origin,
+                         SkAlphaType alpha_type,
+                         uint32_t usage,
+                         gpu::Mailbox& mailbox,
+                         gpu::SyncToken& sync_token) override {
+    mailbox = sii_in_process_->CreateSharedImage(
+        si_format, gpu_memory_buffer->GetSize(), color_space, surface_origin,
+        alpha_type, usage, "VizGmbVideoFramePool",
+        gpu_memory_buffer->CloneHandle());
+    sync_token = sii_in_process_->GenVerifiedSyncToken();
+  }
+
   // Create a SharedImage representation of a plane of a GpuMemoryBuffer
   // allocated by this interface. Populate `mailbox` and `sync_token`.
   void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
diff --git a/content/browser/attribution_reporting/attribution_host_unittest.cc b/content/browser/attribution_reporting/attribution_host_unittest.cc
index fc7be91..c0f3ad30 100644
--- a/content/browser/attribution_reporting/attribution_host_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_host_unittest.cc
@@ -677,7 +677,7 @@
         {blink::ParsedPermissionsPolicyDeclaration(
             blink::mojom::PermissionsPolicyFeature::kAttributionReporting,
             /*allowed_origins=*/
-            {blink::OriginWithPossibleWildcards::FromOrigin(
+            {*blink::OriginWithPossibleWildcards::FromOrigin(
                 url::Origin::Create(GURL(kAllowedOriginUrl)))},
             /*self_if_matches=*/absl::nullopt,
             /*matches_all_origins=*/false, /*matches_opaque_src=*/false)});
diff --git a/content/browser/browsing_topics/browsing_topics_url_loader_unittest.cc b/content/browser/browsing_topics/browsing_topics_url_loader_unittest.cc
index dc771d13..72a5616 100644
--- a/content/browser/browsing_topics/browsing_topics_url_loader_unittest.cc
+++ b/content/browser/browsing_topics/browsing_topics_url_loader_unittest.cc
@@ -166,15 +166,14 @@
     policy.emplace_back(
         blink::mojom::PermissionsPolicyFeature::kBrowsingTopics,
         /*allowed_origins=*/
-        std::vector<blink::OriginWithPossibleWildcards>{
-            blink::OriginWithPossibleWildcards::FromOrigin(
-                url::Origin::Create(GURL("https://google.com"))),
-            blink::OriginWithPossibleWildcards::FromOrigin(
-                url::Origin::Create(GURL("https://foo1.com"))),
-            blink::OriginWithPossibleWildcards::FromOrigin(
-                url::Origin::Create(GURL("https://foo2.com"))),
-            blink::OriginWithPossibleWildcards::FromOrigin(
-                url::Origin::Create(GURL("https://foo3.com")))},
+        std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(
+                        url::Origin::Create(GURL("https://google.com"))),
+                    *blink::OriginWithPossibleWildcards::FromOrigin(
+                        url::Origin::Create(GURL("https://foo1.com"))),
+                    *blink::OriginWithPossibleWildcards::FromOrigin(
+                        url::Origin::Create(GURL("https://foo2.com"))),
+                    *blink::OriginWithPossibleWildcards::FromOrigin(
+                        url::Origin::Create(GURL("https://foo3.com")))},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false);
diff --git a/content/browser/direct_sockets/direct_sockets_test_utils.cc b/content/browser/direct_sockets/direct_sockets_test_utils.cc
index 1934d37..dda32b6 100644
--- a/content/browser/direct_sockets/direct_sockets_test_utils.cc
+++ b/content/browser/direct_sockets/direct_sockets_test_utils.cc
@@ -176,7 +176,7 @@
   blink::ParsedPermissionsPolicyDeclaration decl(
       blink::mojom::PermissionsPolicyFeature::kDirectSockets,
       /*allowed_origins=*/
-      {blink::OriginWithPossibleWildcards::FromOrigin(app_origin)},
+      {*blink::OriginWithPossibleWildcards::FromOrigin(app_origin)},
       /*self_if_matches=*/absl::nullopt,
       /*matches_all_origins=*/false, /*matches_opaque_src=*/false);
   out.push_back(decl);
diff --git a/content/browser/geolocation/geolocation_service_impl_unittest.cc b/content/browser/geolocation/geolocation_service_impl_unittest.cc
index 19efd47..39588cf 100644
--- a/content/browser/geolocation/geolocation_service_impl_unittest.cc
+++ b/content/browser/geolocation/geolocation_service_impl_unittest.cc
@@ -109,7 +109,7 @@
     if (allow_via_permissions_policy) {
       frame_policy.push_back(
           {blink::mojom::PermissionsPolicyFeature::kGeolocation,
-           std::vector{blink::OriginWithPossibleWildcards::FromOrigin(
+           std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(
                url::Origin::Create(kEmbeddedUrl))},
            /*self_if_matches=*/absl::nullopt,
            /*matches_all_origins=*/false,
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 92d858bd..c618429 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -6668,8 +6668,7 @@
   policy.emplace_back(
       blink::mojom::PermissionsPolicyFeature::kSharedStorage,
       /*allowed_origins=*/
-      std::vector<blink::OriginWithPossibleWildcards>{
-          blink::OriginWithPossibleWildcards::FromOrigin(kOriginA)},
+      std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(kOriginA)},
       /*self_if_matches=*/absl::nullopt,
       /*matches_all_origins=*/false,
       /*matches_opaque_src=*/false);
@@ -6882,8 +6881,7 @@
     policy.emplace_back(
         blink::mojom::PermissionsPolicyFeature::kPrivateAggregation,
         /*allowed_origins=*/
-        std::vector<blink::OriginWithPossibleWildcards>{
-            blink::OriginWithPossibleWildcards::FromOrigin(kOriginA)},
+        std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(kOriginA)},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false);
@@ -6908,9 +6906,8 @@
     policy.emplace_back(
         blink::mojom::PermissionsPolicyFeature::kPrivateAggregation,
         /*allowed_origins=*/
-        std::vector<blink::OriginWithPossibleWildcards>{
-            blink::OriginWithPossibleWildcards::FromOrigin(kOriginA),
-            blink::OriginWithPossibleWildcards::FromOrigin(kOriginC)},
+        std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(kOriginA),
+                    *blink::OriginWithPossibleWildcards::FromOrigin(kOriginC)},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false);
@@ -6969,8 +6966,7 @@
     policy.emplace_back(
         blink::mojom::PermissionsPolicyFeature::kPrivateAggregation,
         /*allowed_origins=*/
-        std::vector<blink::OriginWithPossibleWildcards>{
-            blink::OriginWithPossibleWildcards::FromOrigin(kOriginA)},
+        std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(kOriginA)},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false);
@@ -6995,9 +6991,8 @@
     policy.emplace_back(
         blink::mojom::PermissionsPolicyFeature::kPrivateAggregation,
         /*allowed_origins=*/
-        std::vector<blink::OriginWithPossibleWildcards>{
-            blink::OriginWithPossibleWildcards::FromOrigin(kOriginA),
-            blink::OriginWithPossibleWildcards::FromOrigin(kOriginC)},
+        std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(kOriginA),
+                    *blink::OriginWithPossibleWildcards::FromOrigin(kOriginC)},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false);
diff --git a/content/browser/interest_group/ad_auction_url_loader_interceptor_unittest.cc b/content/browser/interest_group/ad_auction_url_loader_interceptor_unittest.cc
index 630e6c66..c38c2e61 100644
--- a/content/browser/interest_group/ad_auction_url_loader_interceptor_unittest.cc
+++ b/content/browser/interest_group/ad_auction_url_loader_interceptor_unittest.cc
@@ -120,11 +120,10 @@
     policy.emplace_back(
         blink::mojom::PermissionsPolicyFeature::kRunAdAuction,
         /*allowed_origins=*/
-        std::vector<blink::OriginWithPossibleWildcards>{
-            blink::OriginWithPossibleWildcards::FromOrigin(
-                url::Origin::Create(GURL("https://google.com"))),
-            blink::OriginWithPossibleWildcards::FromOrigin(
-                url::Origin::Create(GURL("https://foo1.com")))},
+        std::vector{*blink::OriginWithPossibleWildcards::FromOrigin(
+                        url::Origin::Create(GURL("https://google.com"))),
+                    *blink::OriginWithPossibleWildcards::FromOrigin(
+                        url::Origin::Create(GURL("https://foo1.com")))},
         /*self_if_matches=*/absl::nullopt,
         /*matches_all_origins=*/false,
         /*matches_opaque_src=*/false);
diff --git a/content/browser/media/media_devices_permission_checker_unittest.cc b/content/browser/media/media_devices_permission_checker_unittest.cc
index ec367fd..b692f57 100644
--- a/content/browser/media/media_devices_permission_checker_unittest.cc
+++ b/content/browser/media/media_devices_permission_checker_unittest.cc
@@ -57,7 +57,7 @@
     std::vector<blink::OriginWithPossibleWildcards> allowlist;
     if (enabled) {
       allowlist.emplace_back(
-          blink::OriginWithPossibleWildcards::FromOrigin(origin_));
+          *blink::OriginWithPossibleWildcards::FromOrigin(origin_));
     }
     navigation->SetPermissionsPolicyHeader({{feature, allowlist,
                                              /*self_if_matches=*/absl::nullopt,
diff --git a/content/browser/mojo_binder_policy_map_impl.cc b/content/browser/mojo_binder_policy_map_impl.cc
index 3997a7a..665b3c4 100644
--- a/content/browser/mojo_binder_policy_map_impl.cc
+++ b/content/browser/mojo_binder_policy_map_impl.cc
@@ -13,6 +13,7 @@
 #include "device/gamepad/public/mojom/gamepad.mojom.h"
 #include "media/mojo/mojom/media_player.mojom.h"
 #include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
+#include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h"
 #include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h"
@@ -121,6 +122,10 @@
   // safe to allow a prerendered document to use it.
   map.SetAssociatedPolicy<blink::mojom::DisplayCutoutHost>(
       MojoBinderAssociatedPolicy::kGrant);
+
+  // Prerendering pages are allowed to create urls for blobs.
+  map.SetAssociatedPolicy<blink::mojom::BlobURLStore>(
+      MojoBinderAssociatedPolicy::kGrant);
 }
 
 // Register mojo binder policies for same-origin prerendering for content/
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index ebb48dc..4392ebd6 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -6498,6 +6498,122 @@
       shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
 }
 
+// Tests when a back navigation is pending commit, then another back navigation
+// starts.
+IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
+                       MultipleBackNavigation) {
+  // This test expects the document is freshly loaded on the back navigation.
+  DisableBackForwardCacheForTesting(web_contents(),
+                                    BackForwardCache::TEST_REQUIRES_NO_CACHING);
+
+  // Navigate to a.com, then b.com.
+  const GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+  const GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
+  ASSERT_TRUE(NavigateToURL(shell(), url_a));
+  ASSERT_TRUE(NavigateToURL(shell(), url_b));
+
+  // Prior to implementing the UndoCommitNavigation() workaround, the race
+  // condition being tested would result in a crash in the b.com renderer. Open
+  // another b.com window in the same browsing instance to verify that the b.com
+  // renderer does not unexpectedly crash even if the b.com speculative
+  // RenderFrameHost is discarded.
+  ASSERT_TRUE(ExecJs(shell(), JsReplace("window.open($1)", url_b)));
+  ASSERT_EQ(2u, Shell::windows().size());
+  WebContentsImpl* new_web_contents =
+      static_cast<WebContentsImpl*>(Shell::windows()[1]->web_contents());
+  EXPECT_TRUE(WaitForLoadStop(new_web_contents));
+  RenderProcessHost* const b_com_render_process_host =
+      new_web_contents->GetPrimaryMainFrame()->GetProcess();
+
+  // Navigate to c.com.
+  const GURL url_c = embedded_test_server()->GetURL("c.com", "/title1.html");
+  ASSERT_TRUE(NavigateToURL(shell(), url_c));
+
+  NavigationLogger logger(shell()->web_contents());
+
+  // Start a back navigation that will create a speculative RFH in the existing
+  // render process for b.com.
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
+  TestNavigationManager first_back_nav(web_contents, url_b);
+  ASSERT_TRUE(ExecJs(shell(), "history.back()"));
+  ASSERT_TRUE(first_back_nav.WaitForResponse());
+  EXPECT_EQ(url_b, root->navigation_request()->GetURL());
+
+  // Ensure the speculative RFH is in the expected process (i.e. the b.com
+  // process that was created for the navigation in the new window earlier).
+  RenderFrameHostImpl* speculative_render_frame_host =
+      root->render_manager()->speculative_frame_host();
+  ASSERT_TRUE(speculative_render_frame_host);
+  EXPECT_EQ(b_com_render_process_host,
+            speculative_render_frame_host->GetProcess());
+
+  // Pause (and potentially ignore, if navigation queueing is disabled) the next
+  // `DidCommitProvisionalLoad()` for b.com.
+  CommitNavigationPauser commit_pauser(speculative_render_frame_host);
+  first_back_nav.ResumeNavigation();
+  commit_pauser.WaitForCommitAndPause();
+
+  // Now begin a new back navigation while the previous back navigation above is
+  // paused in the pending commit state.
+  absl::optional<ResumeCommitClosureSetWaiter>
+      second_back_nav_resume_commit_closure_set_waiter;
+  if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
+    // If navigation queueing is enabled, the test should verify that a resume
+    // commit closure is actually set. Install a watcher now, before beginning
+    // the second back navigation, since the resume commit closure may be
+    // synchronously set while handling the `BeginNavigation()` IPC in the
+    // browser.
+    OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
+      second_back_nav_resume_commit_closure_set_waiter.emplace(handle);
+    });
+  }
+
+  TestNavigationManager second_back_nav(web_contents, url_a);
+  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  controller.GoBack();
+  ASSERT_TRUE(second_back_nav.WaitForRequestStart());
+  EXPECT_EQ(url_a, root->navigation_request()->GetURL());
+
+  if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
+    // The second back navigation should be queued.
+    second_back_nav.ResumeNavigation();
+    second_back_nav_resume_commit_closure_set_waiter->Wait();
+    // Continue the first navigation's commit.
+    commit_pauser.ResumePausedCommit();
+  }
+
+  // After all the navigations finished, we will end up in a.com.
+  EXPECT_TRUE(second_back_nav.WaitForNavigationFinished());
+  EXPECT_EQ(url_a, web_contents->GetLastCommittedURL());
+
+  // Check the order of navigations finishing.
+  auto results = logger.results();
+  ASSERT_EQ(2u, results.size());
+
+  if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
+    // When navigation queueing is enabled, the pending commit back navigation
+    // to b.com won't get canceled when the second back navigation starts. After
+    // continuing the second back navigation, it finishes and commits
+    // successfully to a.com.
+    EXPECT_EQ(url_b, results[0].url);
+    EXPECT_TRUE(results[0].committed);
+    EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
+  } else {
+    // When navigation queueing is disabled, the pending commit back navigation
+    // to b.com gets canceled when the second back navigation starts. Then the
+    // second back navigation will successfully commit to a.com.
+    EXPECT_EQ(url_b, results[0].url);
+    EXPECT_FALSE(results[0].committed);
+  }
+
+  EXPECT_EQ(url_a, results[1].url);
+  EXPECT_TRUE(results[1].committed);
+  EXPECT_EQ(embedded_test_server()->GetOrigin("a.com"), results[1].origin);
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          CommitNavigationRaceBrowserTest,
                          ::testing::Bool(),
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 2303c242..f858d9f 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/blink/public/common/input/web_gesture_event.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/blink/blink_features.h"
 
 using blink::WebInputEvent;
@@ -258,6 +259,9 @@
 
     case WebInputEvent::Type::kGestureTapDown:
       gesture_sequence_in_progress_ = true;
+      allow_cursor_control_ =
+          !::features::IsTouchTextEditingRedesignEnabled() ||
+          gesture_event->data.tap_down.tap_down_count <= 1;
       if (allowed_touch_action_.has_value())
         gesture_sequence_.append("AY");
       else
diff --git a/content/browser/renderer_host/render_frame_host_permissions_policy_unittest.cc b/content/browser/renderer_host/render_frame_host_permissions_policy_unittest.cc
index 30ac410..755d891 100644
--- a/content/browser/renderer_host/render_frame_host_permissions_policy_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_permissions_policy_unittest.cc
@@ -89,7 +89,7 @@
     result[0].feature = feature;
     for (auto const& origin : origins) {
       result[0].allowed_origins.emplace_back(
-          blink::OriginWithPossibleWildcards::FromOrigin(
+          *blink::OriginWithPossibleWildcards::FromOrigin(
               url::Origin::Create(GURL(origin))));
     }
     return result;
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index bef7961f..c611cb3 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -675,6 +675,12 @@
       delegate_->selection_controller()->HandleLongPressEvent(
           event->time_stamp(), event->location_f());
       break;
+    case ui::ET_GESTURE_TAP_DOWN:
+      if (event->details().tap_down_count() == 2) {
+        delegate_->selection_controller()->HandleDoublePressEvent(
+            event->time_stamp(), event->location_f());
+      }
+      break;
     case ui::ET_GESTURE_TAP:
       delegate_->selection_controller()->HandleTapEvent(
           event->location_f(), event->details().tap_count());
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 3a75219..ac9e95c 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -383,7 +383,7 @@
 
   for (const auto& origin : origins)
     declaration.allowed_origins.emplace_back(
-        blink::OriginWithPossibleWildcards::FromOrigin(
+        *blink::OriginWithPossibleWildcards::FromOrigin(
             url::Origin::Create(origin)));
 
   std::sort(declaration.allowed_origins.begin(),
diff --git a/content/browser/smart_card/smart_card_browsertest.cc b/content/browser/smart_card/smart_card_browsertest.cc
index 7eeab12..ed1efa5 100644
--- a/content/browser/smart_card/smart_card_browsertest.cc
+++ b/content/browser/smart_card/smart_card_browsertest.cc
@@ -1110,4 +1110,226 @@
     })())"));
 }
 
+IN_PROC_BROWSER_TEST_F(SmartCardTest, ListReaders) {
+  MockSmartCardContextFactory& mock_context_factory =
+      GetFakeSmartCardDelegate().mock_context_factory;
+
+  EXPECT_CALL(mock_context_factory, ListReaders(_))
+      .WillOnce([](SmartCardContext::ListReadersCallback callback) {
+        std::vector<std::string> readers{"Foo", "Bar"};
+        auto result =
+            device::mojom::SmartCardListReadersResult::NewReaders(readers);
+        std::move(callback).Run(std::move(result));
+      });
+
+  ASSERT_TRUE(NavigateToURL(shell(), GetIsolatedContextUrl()));
+
+  auto expected_reader_names =
+      base::Value(base::Value::List().Append("Foo").Append("Bar"));
+
+  EXPECT_EQ(expected_reader_names, EvalJs(shell(), R"((async () => {
+       let context = await navigator.smartCard.establishContext();
+       return await context.listReaders();
+     })())"));
+}
+
+IN_PROC_BROWSER_TEST_F(SmartCardTest, GetStatusChange) {
+  MockSmartCardContextFactory& mock_context_factory =
+      GetFakeSmartCardDelegate().mock_context_factory;
+
+  EXPECT_CALL(mock_context_factory,
+              GetStatusChange(base::TimeDelta::Max(), _, _))
+      .WillOnce(
+          [](base::TimeDelta timeout,
+             std::vector<device::mojom::SmartCardReaderStateInPtr> states_in,
+             SmartCardContext::GetStatusChangeCallback callback) {
+            ASSERT_EQ(states_in.size(), 1u);
+            ASSERT_EQ(states_in[0]->reader, "Fake Reader");
+            EXPECT_FALSE(states_in[0]->current_state->unaware);
+            EXPECT_FALSE(states_in[0]->current_state->ignore);
+            EXPECT_FALSE(states_in[0]->current_state->changed);
+            EXPECT_FALSE(states_in[0]->current_state->unknown);
+            EXPECT_FALSE(states_in[0]->current_state->unavailable);
+            EXPECT_TRUE(states_in[0]->current_state->empty);
+            EXPECT_FALSE(states_in[0]->current_state->present);
+            EXPECT_FALSE(states_in[0]->current_state->exclusive);
+            EXPECT_FALSE(states_in[0]->current_state->inuse);
+            EXPECT_FALSE(states_in[0]->current_state->mute);
+            EXPECT_FALSE(states_in[0]->current_state->unpowered);
+
+            auto state_flags = SmartCardReaderStateFlags::New();
+            state_flags->unaware = false;
+            state_flags->ignore = false;
+            state_flags->changed = false;
+            state_flags->unknown = false;
+            state_flags->unavailable = false;
+            state_flags->empty = false;
+            state_flags->present = true;
+            state_flags->exclusive = false;
+            state_flags->inuse = true;
+            state_flags->mute = false;
+            state_flags->unpowered = false;
+
+            std::vector<SmartCardReaderStateOutPtr> states_out;
+            states_out.push_back(SmartCardReaderStateOut::New(
+                "Fake Reader", std::move(state_flags),
+                std::vector<uint8_t>({1u, 2u, 3u, 4u})));
+            auto result =
+                device::mojom::SmartCardStatusChangeResult::NewReaderStates(
+                    std::move(states_out));
+            std::move(callback).Run(std::move(result));
+          });
+
+  ASSERT_TRUE(NavigateToURL(shell(), GetIsolatedContextUrl()));
+
+  EXPECT_EQ(
+      "Fake Reader, {unaware=false, ignore=false, changed=false, "
+      "unknown=false, unavailable=false, empty=false, present=true, "
+      "exclusive=false, inuse=true, mute=false, unpowered=false}, {1,2,3,4}",
+      EvalJs(shell(), R"((async () => {
+       let context = await navigator.smartCard.establishContext();
+
+       let readerStates = [{readerName: "Fake Reader",
+                            currentState: {empty: true}}];
+       let statesOut = await context.getStatusChange(
+           readerStates,
+           AbortSignal.timeout(4321));
+
+       if (statesOut.length !== 1) {
+         return `states array has size ${statesOut.length}`;
+       }
+       let atrString = new Uint8Array(statesOut[0].answerToReset).toString();
+
+       let flags = statesOut[0].eventState;
+       let eventStateString = `unaware=${flags.unaware}`
+           + `, ignore=${flags.ignore}`
+           + `, changed=${flags.changed}`
+           + `, unknown=${flags.unknown}`
+           + `, unavailable=${flags.unavailable}`
+           + `, empty=${flags.empty}`
+           + `, present=${flags.present}`
+           + `, exclusive=${flags.exclusive}`
+           + `, inuse=${flags.inuse}`
+           + `, mute=${flags.mute}`
+           + `, unpowered=${flags.unpowered}`;
+
+       return `${statesOut[0].readerName}, {${eventStateString}}` +
+         `, {${atrString}}`;
+     })())"));
+}
+
+IN_PROC_BROWSER_TEST_F(SmartCardTest, GetStatusChangeAborted) {
+  MockSmartCardContextFactory& mock_context_factory =
+      GetFakeSmartCardDelegate().mock_context_factory;
+
+  base::test::TestFuture<SmartCardContext::GetStatusChangeCallback>
+      get_status_callback;
+
+  {
+    InSequence s;
+
+    EXPECT_CALL(mock_context_factory,
+                GetStatusChange(base::TimeDelta::Max(), _, _))
+        .WillOnce(
+            [&get_status_callback](
+                base::TimeDelta timeout,
+                std::vector<device::mojom::SmartCardReaderStateInPtr> states_in,
+                SmartCardContext::GetStatusChangeCallback callback) {
+              ASSERT_EQ(states_in.size(), size_t(1));
+              ASSERT_EQ(states_in[0]->reader, "Fake Reader");
+              EXPECT_FALSE(states_in[0]->current_state->unaware);
+              EXPECT_FALSE(states_in[0]->current_state->ignore);
+              EXPECT_FALSE(states_in[0]->current_state->changed);
+              EXPECT_FALSE(states_in[0]->current_state->unknown);
+              EXPECT_FALSE(states_in[0]->current_state->unavailable);
+              EXPECT_TRUE(states_in[0]->current_state->empty);
+              EXPECT_FALSE(states_in[0]->current_state->present);
+              EXPECT_FALSE(states_in[0]->current_state->exclusive);
+              EXPECT_FALSE(states_in[0]->current_state->inuse);
+              EXPECT_FALSE(states_in[0]->current_state->mute);
+              EXPECT_FALSE(states_in[0]->current_state->unpowered);
+
+              // Don't respond immediately.
+              get_status_callback.SetValue(std::move(callback));
+            });
+
+    // Aborting a blink context.getStatusChange() call means sending a Cancel()
+    // request down to device.mojom.
+    EXPECT_CALL(mock_context_factory, Cancel(_))
+        .WillOnce(
+            [&get_status_callback](SmartCardContext::CancelCallback callback) {
+              std::move(get_status_callback)
+                  .Take()
+                  .Run(device::mojom::SmartCardStatusChangeResult::NewError(
+                      SmartCardError::kCancelled));
+
+              std::move(callback).Run(
+                  SmartCardResult::NewSuccess(SmartCardSuccess::kOk));
+            });
+  }
+
+  ASSERT_TRUE(NavigateToURL(shell(), GetIsolatedContextUrl()));
+
+  EXPECT_EQ("Exception: AbortError", EvalJs(shell(), R"((async () => {
+       let context = await navigator.smartCard.establishContext();
+
+       let abortController = new AbortController();
+
+       let getStatusPromise = context.getStatusChange(
+           [{readerName: "Fake Reader", currentState: {empty: true}}],
+           abortController.signal);
+
+       abortController.abort();
+
+       try {
+         let result = await getStatusPromise;
+         return "Success";
+       } catch (e) {
+         return `Exception: ${e.name}`;
+       }
+     })())"));
+}
+
+IN_PROC_BROWSER_TEST_F(SmartCardTest, ContextConnect) {
+  MockSmartCardContextFactory& mock_context_factory =
+      GetFakeSmartCardDelegate().mock_context_factory;
+
+  EXPECT_CALL(mock_context_factory,
+              Connect("Fake reader", SmartCardShareMode::kShared, _, _))
+      .WillOnce([](const std::string& reader,
+                   device::mojom::SmartCardShareMode share_mode,
+                   device::mojom::SmartCardProtocolsPtr preferred_protocols,
+                   SmartCardContext::ConnectCallback callback) {
+        mojo::PendingRemote<device::mojom::SmartCardConnection> pending_remote;
+
+        EXPECT_TRUE(preferred_protocols->t0);
+        EXPECT_TRUE(preferred_protocols->t1);
+        EXPECT_FALSE(preferred_protocols->raw);
+
+        mojo::MakeSelfOwnedReceiver(
+            std::make_unique<MockSmartCardConnection>(),
+            pending_remote.InitWithNewPipeAndPassReceiver());
+
+        auto success = device::mojom::SmartCardConnectSuccess::New(
+            std::move(pending_remote), SmartCardProtocol::kT1);
+
+        std::move(callback).Run(
+            device::mojom::SmartCardConnectResult::NewSuccess(
+                std::move(success)));
+      });
+
+  ASSERT_TRUE(NavigateToURL(shell(), GetIsolatedContextUrl()));
+
+  auto expected_reader_names =
+      base::Value(base::Value::List().Append("Foo").Append("Bar"));
+
+  EXPECT_EQ("[object SmartCardConnection]", EvalJs(shell(), R"(
+    (async () => {
+      let context = await navigator.smartCard.establishContext();
+      let connection = await context.connect("Fake reader", "shared",
+          ["t0", "t1"]);
+      return `${connection}`;
+    })())"));
+}
+
 }  // namespace content
diff --git a/content/browser/smart_card/smart_card_service.cc b/content/browser/smart_card/smart_card_service.cc
index 89addcf..cc90865 100644
--- a/content/browser/smart_card/smart_card_service.cc
+++ b/content/browser/smart_card/smart_card_service.cc
@@ -49,6 +49,10 @@
                       std::move(callback));
   }
 
+  void CreateContext(CreateContextCallback callback) override {
+    service_->CreateContext(std::move(callback));
+  }
+
  private:
   const std::unique_ptr<SmartCardService> service_;
 };
@@ -137,7 +141,7 @@
   if (!context_) {
     context_ = mojo::Remote<device::mojom::SmartCardContext>();
     context_factory_->CreateContext(
-        base::BindOnce(&SmartCardService::OnCreateContextDone,
+        base::BindOnce(&SmartCardService::OnCreateInternalContextDone,
                        weak_ptr_factory_.GetWeakPtr()));
   }
 
@@ -155,6 +159,10 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void SmartCardService::CreateContext(CreateContextCallback callback) {
+  context_factory_->CreateContext(std::move(callback));
+}
+
 void SmartCardService::OnReaderAdded(
     const blink::mojom::SmartCardReaderInfo& reader_info) {
   for (auto& client : clients_) {
@@ -182,7 +190,7 @@
   }
 }
 
-void SmartCardService::OnCreateContextDone(
+void SmartCardService::OnCreateInternalContextDone(
     device::mojom::SmartCardCreateContextResultPtr result) {
   CHECK(context_ && !context_->is_bound());
 
diff --git a/content/browser/smart_card/smart_card_service.h b/content/browser/smart_card/smart_card_service.h
index 2558f2c..076ff664 100644
--- a/content/browser/smart_card/smart_card_service.h
+++ b/content/browser/smart_card/smart_card_service.h
@@ -46,6 +46,7 @@
                device::mojom::SmartCardShareMode share_mode,
                device::mojom::SmartCardProtocolsPtr preferred_protocols,
                ConnectCallback callback) override;
+  void CreateContext(CreateContextCallback callback) override;
 
   // SmartCardReaderTracker::Observer overrides:
   void OnReaderAdded(
@@ -70,7 +71,7 @@
     SmartCardService::ConnectCallback callback;
   };
 
-  void OnCreateContextDone(
+  void OnCreateInternalContextDone(
       device::mojom::SmartCardCreateContextResultPtr result);
   void OnConnectDone(ConnectCallback callback,
                      device::mojom::SmartCardConnectResultPtr result);
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index bb7f28b..1bfaf56 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -846,9 +846,14 @@
 ShellContentBrowserClient::GetPermissionsPolicyForIsolatedWebApp(
     content::BrowserContext* browser_context,
     const url::Origin& app_origin) {
+  std::vector<blink::OriginWithPossibleWildcards> allowlist;
+  if (auto origin_with_possible_wildcards =
+          blink::OriginWithPossibleWildcards::FromOrigin(app_origin);
+      origin_with_possible_wildcards.has_value()) {
+    allowlist.emplace_back(*origin_with_possible_wildcards);
+  }
   blink::ParsedPermissionsPolicyDeclaration decl(
-      blink::mojom::PermissionsPolicyFeature::kDirectSockets,
-      {blink::OriginWithPossibleWildcards::FromOrigin(app_origin)},
+      blink::mojom::PermissionsPolicyFeature::kDirectSockets, allowlist,
       /*self_if_matches=*/absl::nullopt,
       /*matches_all_origins=*/false, /*matches_opaque_src=*/false);
   return {{decl}};
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc
index b020e54..9ac68f00f 100644
--- a/device/vr/openxr/openxr_api_wrapper.cc
+++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -763,11 +763,12 @@
     gpu_memory_buffer_handle.dxgi_handle.Set(shared_handle);
     gpu_memory_buffer_handle.dxgi_token = gfx::DXGIHandleToken();
     gpu_memory_buffer_handle.type = gfx::DXGI_SHARED_HANDLE;
+    gfx::Size buffer_size =
+        gfx::Size(texture2d_desc.Width, texture2d_desc.Height);
 
     std::unique_ptr<gpu::GpuMemoryBufferImplDXGI> gpu_memory_buffer =
         gpu::GpuMemoryBufferImplDXGI::CreateFromHandle(
-            std::move(gpu_memory_buffer_handle),
-            gfx::Size(texture2d_desc.Width, texture2d_desc.Height),
+            std::move(gpu_memory_buffer_handle), buffer_size,
             gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::GPU_READ,
             base::DoNothing(), nullptr, nullptr);
 
@@ -777,11 +778,11 @@
 
     gpu::MailboxHolder& mailbox_holder = swap_chain_info.mailbox_holder;
     mailbox_holder.mailbox = shared_image_interface->CreateSharedImage(
-        gpu_memory_buffer.get(), nullptr,
+        viz::SinglePlaneFormat::kRGBA_8888, buffer_size,
         gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT709,
                         gfx::ColorSpace::TransferID::LINEAR),
         kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, shared_image_usage,
-        "OpenXrSwapChain");
+        "OpenXrSwapChain", gpu_memory_buffer->CloneHandle());
     mailbox_holder.sync_token = shared_image_interface->GenVerifiedSyncToken();
     mailbox_holder.texture_target = GL_TEXTURE_2D;
   }
diff --git a/docs/dangling_ptr.md b/docs/dangling_ptr.md
index 8ae4cd4..3547439 100644
--- a/docs/dangling_ptr.md
+++ b/docs/dangling_ptr.md
@@ -1,5 +1,8 @@
 # Dangling Pointer Detector
 
+A pointer is dangling when it references freed memory. Typical examples can be
+found [here](https://docs.google.com/document/d/11YYsyPF9rQv_QFf982Khie3YuNPXV0NdhzJPojpZfco/edit?resourcekey=0-h1dr1uDzZGU7YWHth5TRAQ#heading=h.wxt96wl0k0sq).
+
 Dangling pointers are not a problem unless they are subsequently dereferenced
 and/or used for other purposes. Proving that pointers are unused has turned out
 to be difficult in general, especially in face of future modifications to
diff --git a/google_apis/gaia/gaia_constants.cc b/google_apis/gaia/gaia_constants.cc
index 4fc11883..7348de6 100644
--- a/google_apis/gaia/gaia_constants.cc
+++ b/google_apis/gaia/gaia_constants.cc
@@ -121,6 +121,10 @@
 // OAuth2 scope for access to Drive.
 const char kDriveOAuth2Scope[] = "https://www.googleapis.com/auth/drive";
 
+// OAuth2 scope for access for DriveFS to access flags.
+const char kExperimentsAndConfigsOAuth2Scope[] =
+    "https://www.googleapis.com/auth/experimentsandconfigs";
+
 // The scope required for an access token in order to query ItemSuggest.
 const char kDriveReadOnlyOAuth2Scope[] =
     "https://www.googleapis.com/auth/drive.readonly";
diff --git a/google_apis/gaia/gaia_constants.h b/google_apis/gaia/gaia_constants.h
index 1a97150..df83fdb32 100644
--- a/google_apis/gaia/gaia_constants.h
+++ b/google_apis/gaia/gaia_constants.h
@@ -55,6 +55,8 @@
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kPaymentsOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kCryptAuthOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kDriveOAuth2Scope[];
+COMPONENT_EXPORT(GOOGLE_APIS)
+extern const char kExperimentsAndConfigsOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kDriveReadOnlyOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kAssistantOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS)
diff --git a/gpu/command_buffer/service/copy_shared_image_helper.cc b/gpu/command_buffer/service/copy_shared_image_helper.cc
index dc376ec..4560aae 100644
--- a/gpu/command_buffer/service/copy_shared_image_helper.cc
+++ b/gpu/command_buffer/service/copy_shared_image_helper.cc
@@ -732,8 +732,14 @@
       SkYUVAInfo yuva_info(gfx::SizeToSkISize(dest_shared_image->size()),
                            ToSkYUVAPlaneConfig(dest_format),
                            ToSkYUVASubsampling(dest_format), yuv_color_space);
-      // Perform skia::BlitRGBAToYUVA for the multiplanar YUV format image.
-      skia::BlitRGBAToYUVA(source_image.get(), yuva_sk_surfaces, yuva_info);
+      // Perform skia::BlitRGBAToYUVA for the multiplanar YUV format image,
+      // having it clear the destination image if necessary and then populate
+      // |dest_rect|.
+      skia::BlitRGBAToYUVA(
+          source_image.get(), yuva_sk_surfaces, yuva_info,
+          gfx::RectToSkRect(dest_rect),
+          /*clear_destination=*/!dest_shared_image->IsCleared());
+      dest_shared_image->SetCleared();
     }
 
     if (!dest_shared_image->IsCleared()) {
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index a38f9251..fdc020ec 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -1131,7 +1131,8 @@
       static_cast<WGPUBackendType>(adapter_properties.backendType), {});
   return SkiaGraphiteDawnImageRepresentation::Create(
       std::move(dawn_representation), context_state,
-      context_state->gpu_main_graphite_recorder(), manager, this, tracker);
+      context_state->gpu_main_graphite_recorder(), manager, this, tracker,
+      static_cast<int>(plane_index_));
 #else
   return nullptr;
 #endif
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
index 3d3a367..b185d2d 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -1027,7 +1027,8 @@
     // fulfilling Graphite promise images on GPU main thread.
     return SkiaGraphiteDawnImageRepresentation::Create(
         std::move(dawn_representation), context_state,
-        context_state->gpu_main_graphite_recorder(), manager, this, tracker);
+        context_state->gpu_main_graphite_recorder(), manager, this, tracker,
+        static_cast<int>(io_surface_plane_));
 #endif
   } else {
     CHECK_EQ(context_state->gr_context_type(), GrContextType::kGraphiteMetal);
diff --git a/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
index 0164581..c3939ba 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
@@ -270,7 +270,7 @@
       wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc;
 
   // The below usages are not supported for multiplanar formats in Dawn.
-  if (format.is_single_plane()) {
+  if (format.is_single_plane() && !format.IsLegacyMultiplanar()) {
     usage |= wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst;
   }
 
@@ -293,21 +293,30 @@
   } else {
     CHECK_EQ(gr_context_type, GrContextType::kGraphiteDawn);
 #if BUILDFLAG(SKIA_USE_DAWN)
-    // TODO(crbug.com/1445450): Add support for multiplanar formats, passing
-    // |plane_index|.
-    wgpu::TextureFormat wgpu_format = ToDawnFormat(format, plane_index);
-    if (wgpu_format != wgpu::TextureFormat::Undefined) {
-      skgpu::graphite::DawnTextureInfo dawn_texture_info;
-      dawn_texture_info.fSampleCount = 1;
-      dawn_texture_info.fFormat = wgpu_format;
-      dawn_texture_info.fUsage = GetSupportedDawnTextureUsage(format);
-      dawn_texture_info.fMipmapped =
-          mipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
-      return dawn_texture_info;
-    }
+    return GetGraphiteDawnTextureInfo(format, plane_index, mipmapped);
 #endif
   }
   NOTREACHED_NORETURN();
 }
 
+#if BUILDFLAG(SKIA_USE_DAWN)
+skgpu::graphite::DawnTextureInfo GetGraphiteDawnTextureInfo(
+    viz::SharedImageFormat format,
+    int plane_index,
+    bool mipmapped) {
+  skgpu::graphite::DawnTextureInfo dawn_texture_info;
+  // TODO(crbug.com/1445450): Add support for multiplanar formats, passing
+  // |plane_index|.
+  wgpu::TextureFormat wgpu_format = ToDawnFormat(format, plane_index);
+  if (wgpu_format != wgpu::TextureFormat::Undefined) {
+    dawn_texture_info.fSampleCount = 1;
+    dawn_texture_info.fFormat = wgpu_format;
+    dawn_texture_info.fUsage = GetSupportedDawnTextureUsage(format);
+    dawn_texture_info.fMipmapped =
+        mipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
+  }
+  return dawn_texture_info;
+}
+#endif
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h
index 9a43b377..cd86c2c 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h
@@ -124,6 +124,13 @@
     int plane_index = 0,
     bool mipmapped = false);
 
+#if BUILDFLAG(SKIA_USE_DAWN)
+GPU_GLES2_EXPORT skgpu::graphite::DawnTextureInfo GetGraphiteDawnTextureInfo(
+    viz::SharedImageFormat format,
+    int plane_index = 0,
+    bool mipmapped = false);
+#endif
+
 #if BUILDFLAG(SKIA_USE_METAL)
 GPU_GLES2_EXPORT skgpu::graphite::MtlTextureInfo GetGraphiteMetalTextureInfo(
     viz::SharedImageFormat format,
diff --git a/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.cc
index 299ff4b..8bc08bd9 100644
--- a/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.cc
@@ -14,11 +14,25 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/graphite/Recorder.h"
 #include "third_party/skia/include/gpu/graphite/Surface.h"
-
-#include <webgpu/webgpu.h>
+#include "ui/gfx/geometry/skia_conversions.h"
 
 namespace gpu {
 
+namespace {
+wgpu::TextureView CreatePlaneView(const wgpu::Texture& texture,
+                                  int plane_index) {
+  CHECK_EQ(texture.GetFormat(), wgpu::TextureFormat::R8BG8Biplanar420Unorm);
+  wgpu::TextureViewDescriptor view_desc;
+  if (plane_index == 0) {
+    view_desc.aspect = wgpu::TextureAspect::Plane0Only;
+  } else {
+    CHECK_EQ(plane_index, 1);
+    view_desc.aspect = wgpu::TextureAspect::Plane1Only;
+  }
+  return texture.CreateView(&view_desc);
+}
+}  // namespace
+
 // static method.
 std::unique_ptr<SkiaGraphiteDawnImageRepresentation>
 SkiaGraphiteDawnImageRepresentation::Create(
@@ -27,10 +41,11 @@
     skgpu::graphite::Recorder* recorder,
     SharedImageManager* manager,
     SharedImageBacking* backing,
-    MemoryTypeTracker* tracker) {
+    MemoryTypeTracker* tracker,
+    int plane_index) {
   return base::WrapUnique(new SkiaGraphiteDawnImageRepresentation(
       std::move(dawn_representation), recorder, std::move(context_state),
-      manager, backing, tracker));
+      manager, backing, tracker, plane_index));
 }
 
 SkiaGraphiteDawnImageRepresentation::SkiaGraphiteDawnImageRepresentation(
@@ -39,11 +54,13 @@
     scoped_refptr<SharedContextState> context_state,
     SharedImageManager* manager,
     SharedImageBacking* backing,
-    MemoryTypeTracker* tracker)
+    MemoryTypeTracker* tracker,
+    int plane_index)
     : SkiaGraphiteImageRepresentation(manager, backing, tracker),
       dawn_representation_(std::move(dawn_representation)),
       context_state_(std::move(context_state)),
-      recorder_(recorder) {
+      recorder_(recorder),
+      plane_index_(plane_index) {
   CHECK(dawn_representation_);
 }
 
@@ -124,12 +141,45 @@
     DLOG(ERROR) << "Could not create DawnImageRepresentation::ScopedAccess";
     return {};
   }
+
   mode_ = RepresentationAccessMode::kRead;
-  return {skgpu::graphite::BackendTexture(dawn_scoped_access_->texture())};
+
+  wgpu::Texture texture(dawn_scoped_access_->texture());
+  std::vector<skgpu::graphite::BackendTexture> backend_textures;
+  CHECK(plane_views_.empty());
+  if (format() == viz::MultiPlaneFormat::kNV12) {
+    backend_textures.reserve(format().NumberOfPlanes());
+    plane_views_.reserve(format().NumberOfPlanes());
+    for (int plane_index = 0; plane_index < format().NumberOfPlanes();
+         plane_index++) {
+      wgpu::TextureView plane_view = CreatePlaneView(texture, plane_index);
+      SkISize plane_size =
+          gfx::SizeToSkISize(format().GetPlaneSize(plane_index, size()));
+      skgpu::graphite::DawnTextureInfo plane_info =
+          GetGraphiteDawnTextureInfo(format(), plane_index);
+      backend_textures.emplace_back(plane_size, plane_info, plane_view.Get());
+      plane_views_.push_back(std::move(plane_view));
+    }
+  } else if (texture.GetFormat() ==
+             wgpu::TextureFormat::R8BG8Biplanar420Unorm) {
+    // Legacy multi-planar NV12 - format() is either R8 or RG8.
+    wgpu::TextureView plane_view = CreatePlaneView(texture, plane_index_);
+    SkISize plane_size = gfx::SizeToSkISize(size());
+    skgpu::graphite::DawnTextureInfo plane_info =
+        GetGraphiteDawnTextureInfo(format());
+    backend_textures = {skgpu::graphite::BackendTexture(plane_size, plane_info,
+                                                        plane_view.Get())};
+    plane_views_ = {std::move(plane_view)};
+  } else {
+    CHECK(format().is_single_plane() && !format().IsLegacyMultiplanar());
+    backend_textures = {skgpu::graphite::BackendTexture(texture.Get())};
+  }
+  return backend_textures;
 }
 
 void SkiaGraphiteDawnImageRepresentation::EndReadAccess() {
   CHECK_EQ(mode_, RepresentationAccessMode::kRead);
+  plane_views_.clear();
   dawn_scoped_access_.reset();
   mode_ = RepresentationAccessMode::kNone;
 }
diff --git a/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.h b/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.h
index 62ebec8..5223eb9 100644
--- a/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.h
+++ b/gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.h
@@ -8,6 +8,8 @@
 #include "base/memory/raw_ptr.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 
+#include <webgpu/webgpu_cpp.h>
+
 namespace gpu {
 // This is a wrapper class for SkiaGraphiteImageRepresentation to be used in
 // Dawn mode.
@@ -20,7 +22,8 @@
       skgpu::graphite::Recorder* recorder,
       SharedImageManager* manager,
       SharedImageBacking* backing,
-      MemoryTypeTracker* tracker);
+      MemoryTypeTracker* tracker,
+      int plane_index = 0);
 
   ~SkiaGraphiteDawnImageRepresentation() override;
 
@@ -40,13 +43,16 @@
       scoped_refptr<SharedContextState> context_state,
       SharedImageManager* manager,
       SharedImageBacking* backing,
-      MemoryTypeTracker* tracker);
+      MemoryTypeTracker* tracker,
+      int plane_index);
 
   std::unique_ptr<DawnImageRepresentation> dawn_representation_;
   std::unique_ptr<DawnImageRepresentation::ScopedAccess> dawn_scoped_access_;
   scoped_refptr<SharedContextState> context_state_;
-  raw_ptr<skgpu::graphite::Recorder> recorder_ = nullptr;
+  const raw_ptr<skgpu::graphite::Recorder> recorder_;
+  const int plane_index_;
   RepresentationAccessMode mode_ = RepresentationAccessMode::kNone;
+  std::vector<wgpu::TextureView> plane_views_;
 };
 
 }  // namespace gpu
diff --git a/ios/chrome/browser/autofill/message/BUILD.gn b/ios/chrome/browser/autofill/message/BUILD.gn
new file mode 100644
index 0000000..46b11c99
--- /dev/null
+++ b/ios/chrome/browser/autofill/message/BUILD.gn
@@ -0,0 +1,12 @@
+# 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.
+
+source_set("message") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "save_card_message_with_links.h",
+    "save_card_message_with_links.mm",
+  ]
+  deps = [ "//url:url" ]
+}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/autofill/save_card_message_with_links.h b/ios/chrome/browser/autofill/message/save_card_message_with_links.h
similarity index 71%
rename from ios/chrome/browser/ui/autofill/save_card_message_with_links.h
rename to ios/chrome/browser/autofill/message/save_card_message_with_links.h
index 0baf1ef..264cdf6 100644
--- a/ios/chrome/browser/ui/autofill/save_card_message_with_links.h
+++ b/ios/chrome/browser/autofill/message/save_card_message_with_links.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_AUTOFILL_SAVE_CARD_MESSAGE_WITH_LINKS_H_
-#define IOS_CHROME_BROWSER_UI_AUTOFILL_SAVE_CARD_MESSAGE_WITH_LINKS_H_
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_MESSAGE_SAVE_CARD_MESSAGE_WITH_LINKS_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_MESSAGE_SAVE_CARD_MESSAGE_WITH_LINKS_H_
 
 #import <UIKit/UIKit.h>
 
@@ -24,4 +24,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_SAVE_CARD_MESSAGE_WITH_LINKS_H_
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_MESSAGE_SAVE_CARD_MESSAGE_WITH_LINKS_H_
diff --git a/ios/chrome/browser/ui/autofill/save_card_message_with_links.mm b/ios/chrome/browser/autofill/message/save_card_message_with_links.mm
similarity index 80%
rename from ios/chrome/browser/ui/autofill/save_card_message_with_links.mm
rename to ios/chrome/browser/autofill/message/save_card_message_with_links.mm
index 215938d..380cde5 100644
--- a/ios/chrome/browser/ui/autofill/save_card_message_with_links.mm
+++ b/ios/chrome/browser/autofill/message/save_card_message_with_links.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/autofill/save_card_message_with_links.h"
+#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
 
 #import "url/gurl.h"
 
diff --git a/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.h b/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.h
index 383c8b2..55dc00b 100644
--- a/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.h
+++ b/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.h
@@ -35,6 +35,10 @@
 - (void)bookmarkModelRemovedAllNodes:(bookmarks::BookmarkModel*)model;
 
 @optional
+// Called before removing a bookmark node.
+- (void)bookmarkModel:(bookmarks::BookmarkModel*)model
+       willDeleteNode:(const bookmarks::BookmarkNode*)node
+           fromFolder:(const bookmarks::BookmarkNode*)folder;
 // Called before removing all non-permanent nodes.
 - (void)bookmarkModelWillRemoveAllNodes:(const bookmarks::BookmarkModel*)model;
 // The node favicon changed.
@@ -63,6 +67,10 @@
                          const bookmarks::BookmarkNode* parent,
                          size_t index,
                          bool added_by_user) override;
+  void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
+                             const bookmarks::BookmarkNode* parent,
+                             size_t old_index,
+                             const bookmarks::BookmarkNode* node) override;
   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                            const bookmarks::BookmarkNode* parent,
                            size_t old_index,
diff --git a/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.mm b/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.mm
index 95e00f0..730a32e0 100644
--- a/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.mm
+++ b/ios/chrome/browser/bookmarks/bookmark_model_bridge_observer.mm
@@ -59,6 +59,18 @@
   [observer_ bookmarkModel:model didChangeChildrenForNode:parent];
 }
 
+void BookmarkModelBridge::OnWillRemoveBookmarks(
+    bookmarks::BookmarkModel* model,
+    const bookmarks::BookmarkNode* parent,
+    size_t old_index,
+    const bookmarks::BookmarkNode* node) {
+  CHECK(model_observation_.IsObservingSource(model));
+  SEL selector = @selector(bookmarkModel:willDeleteNode:fromFolder:);
+  if ([observer_ respondsToSelector:selector]) {
+    [observer_ bookmarkModel:model willDeleteNode:node fromFolder:parent];
+  }
+}
+
 void BookmarkModelBridge::BookmarkNodeRemoved(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* parent,
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn b/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn
index 9631b37..8e14eff 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn
+++ b/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn
@@ -28,11 +28,11 @@
     "//components/password_manager/core/common:features",
     "//components/translate/core/browser",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/autofill/message",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/common/infobars",
     "//ios/chrome/browser/passwords:infobar_delegates",
-    "//ios/chrome/browser/ui/autofill:autofill_message",
     "//ios/chrome/browser/ui/autofill:autofill_ui_type",
     "//ui/gfx",
   ]
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/DEPS b/ios/chrome/browser/overlays/public/infobar_modal/DEPS
index 51c9428..99b84dd 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/DEPS
+++ b/ios/chrome/browser/overlays/public/infobar_modal/DEPS
@@ -4,7 +4,7 @@
     "+ios/chrome/browser/ui/autofill/autofill_ui_type_util.h",
   ],
   "^save_card_infobar_modal_overlay_request_config.mm": [
-    "+ios/chrome/browser/ui/autofill/save_card_message_with_links.h",
+    "+ios/chrome/browser/shared/model/autofill/save_card_message_with_links.h",
   ],
 
 }
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm b/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm
index aa51511..778736a 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm
+++ b/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm
@@ -7,9 +7,9 @@
 #import "base/check.h"
 #import "base/strings/sys_string_conversions.h"
 #import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
+#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
 #import "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
-#import "ios/chrome/browser/ui/autofill/save_card_message_with_links.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/shared/model/autofill/BUILD.gn b/ios/chrome/browser/shared/model/autofill/BUILD.gn
new file mode 100644
index 0000000..2e75980a
--- /dev/null
+++ b/ios/chrome/browser/shared/model/autofill/BUILD.gn
@@ -0,0 +1,12 @@
+# 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.
+
+source_set("autofill_message") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "save_card_message_with_links.h",
+    "save_card_message_with_links.mm",
+  ]
+  deps = [ "//url:url" ]
+}
diff --git a/ios/chrome/browser/signin/fake_system_identity_interaction_manager.h b/ios/chrome/browser/signin/fake_system_identity_interaction_manager.h
index 030cf4bb..05ad109 100644
--- a/ios/chrome/browser/signin/fake_system_identity_interaction_manager.h
+++ b/ios/chrome/browser/signin/fake_system_identity_interaction_manager.h
@@ -35,6 +35,10 @@
 // Returns whether the activity view is presented.
 @property(nonatomic, readonly) BOOL isActivityViewPresented;
 
+// The user email passed on the last call to
+// `startAuthActivityWithViewController:userEmail:completion:`.
+@property(nonatomic, strong, readonly) NSString* lastStartAuthActivityUserEmail;
+
 // Stores the identity to use when sign-in tap is simulated.Must be non
 // nil before calling `-simulateDidTapAddAccount` method.
 @property(nonatomic, strong, class) id<SystemIdentity> identity;
diff --git a/ios/chrome/browser/signin/fake_system_identity_interaction_manager.mm b/ios/chrome/browser/signin/fake_system_identity_interaction_manager.mm
index f12835a..4ab0a09 100644
--- a/ios/chrome/browser/signin/fake_system_identity_interaction_manager.mm
+++ b/ios/chrome/browser/signin/fake_system_identity_interaction_manager.mm
@@ -98,6 +98,7 @@
   SigninCompletionBlock _signinCompletion;
   FakeAuthActivityViewController* _authActivityViewController;
   BOOL _isActivityViewPresented;
+  NSString* _lastStartAuthActivityUserEmail;
 }
 
 - (instancetype)initWithManager:
@@ -142,6 +143,7 @@
                                   userEmail:(NSString*)userEmail
                                  completion:(SigninCompletionBlock)completion {
   DCHECK(completion);
+  _lastStartAuthActivityUserEmail = userEmail;
   _signinCompletion = completion;
   _authActivityViewController =
       [[FakeAuthActivityViewController alloc] initWithManager:self];
@@ -169,6 +171,10 @@
   return _isActivityViewPresented;
 }
 
+- (NSString*)lastStartAuthActivityUserEmail {
+  return _lastStartAuthActivityUserEmail;
+}
+
 + (id<SystemIdentity>)identity {
   return gFakeSystemIdentityInteractionManagerIdentity;
 }
diff --git a/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager.mm b/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager.mm
index a40ff26..a657abdf 100644
--- a/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager.mm
+++ b/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager.mm
@@ -57,12 +57,10 @@
 - (void)showSigninWithIntent:(AddAccountSigninIntent)signinIntent {
   DCHECK(!_addAccountFlowDone);
   DCHECK(self.identityInteractionManager);
-  NSString* userEmail;
+  NSString* userEmail = nil;
   switch (signinIntent) {
-    case AddAccountSigninIntentAddSecondaryAccount: {
-      userEmail = nil;
+    case AddAccountSigninIntentAddSecondaryAccount:
       break;
-    }
     case AddAccountSigninIntentReauthPrimaryAccount: {
       CoreAccountInfo accountInfo = self.identityManager->GetPrimaryAccountInfo(
           signin::ConsentLevel::kSync);
@@ -79,8 +77,12 @@
         userEmailString =
             self.prefService->GetString(prefs::kGoogleServicesLastUsername);
       }
-      DCHECK(!userEmailString.empty());
-      userEmail = base::SysUTF8ToNSString(userEmailString);
+
+      // Note(crbug/1443096): Gracefully handle an empty `userEmailString` by
+      // showing the sign-in screen without a prefilled email.
+      if (!userEmailString.empty()) {
+        userEmail = base::SysUTF8ToNSString(userEmailString);
+      }
       break;
     }
   }
diff --git a/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager_unittest.mm b/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager_unittest.mm
index c04ca4da..fc4c837 100644
--- a/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_manager_unittest.mm
@@ -7,6 +7,7 @@
 #import <UIKit/UIKit.h>
 
 #import "base/mac/foundation_util.h"
+#import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "base/test/task_environment.h"
 #import "components/prefs/pref_registry_simple.h"
@@ -19,6 +20,7 @@
 #import "ios/chrome/browser/signin/fake_system_identity_manager.h"
 #import "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/web/common/uikit_ui_util.h"
+#import "testing/gtest_mac.h"
 #import "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
@@ -53,17 +55,9 @@
     return IdentityManagerFactory::GetForBrowserState(browser_state_.get());
   }
 
-  // Registers account preferences that will be used in reauthentication.
-  PrefService* GetPrefService() {
-    TestingPrefServiceSimple* prefs = new TestingPrefServiceSimple();
-    PrefRegistrySimple* registry = prefs->registry();
-    registry->RegisterStringPref(prefs::kGoogleServicesLastUsername,
-                                 kTestEmail);
-    registry->RegisterStringPref(prefs::kGoogleServicesLastGaiaId, kTestGaiaID);
-    return prefs;
-  }
-
-  void WaitForFakeAddAccountViewPresented() {
+  void WaitForFakeAddAccountViewPresented(NSString* expectedUserEmail) {
+    EXPECT_NSEQ(expectedUserEmail,
+                identity_interaction_manager_.lastStartAuthActivityUserEmail);
     base::test::ios::WaitUntilCondition(^bool() {
       return identity_interaction_manager_.isActivityViewPresented;
     });
@@ -84,6 +78,10 @@
   void SetUp() override {
     PlatformTest::SetUp();
 
+    PrefService* prefs = browser_state_->GetPrefs();
+    prefs->SetString(prefs::kGoogleServicesLastUsername, kTestEmail);
+    prefs->SetString(prefs::kGoogleServicesLastGaiaId, kTestGaiaID);
+
     base_view_controller_ = [[UIViewController alloc] init];
     base_view_controller_.view.backgroundColor = UIColor.blueColor;
     GetAnyKeyWindow().rootViewController = base_view_controller_;
@@ -91,7 +89,7 @@
     signin_manager_ = [[AddAccountSigninManager alloc]
         initWithBaseViewController:base_view_controller_
         identityInteractionManager:identity_interaction_manager_
-                       prefService:GetPrefService()
+                       prefService:prefs
                    identityManager:GetIdentityManager()];
     signin_manager_delegate_ =
         OCMStrictProtocolMock(@protocol(AddAccountSigninManagerDelegate));
@@ -128,7 +126,7 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentAddSecondaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(/*expectedUserEmail=*/nil);
   [identity_interaction_manager_ simulateDidTapAddAccount];
   WaitForFakeAddAccountViewDismissed();
 }
@@ -145,7 +143,7 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentAddSecondaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(/*expectedUserEmail=*/nil);
   [identity_interaction_manager_ simulateDidTapCancel];
   WaitForFakeAddAccountViewDismissed();
 }
@@ -163,7 +161,7 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentAddSecondaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(/*expectedUserEmail=*/nil);
   [identity_interaction_manager_ simulateDidThrowUnhandledError];
   WaitForFakeAddAccountViewDismissed();
 }
@@ -177,7 +175,7 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentAddSecondaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(/*expectedUserEmail=*/nil);
   __block BOOL completionCalled = NO;
   [signin_manager_ interruptAddAccountAnimated:YES
                                     completion:^() {
@@ -200,7 +198,8 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentReauthPrimaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(
+      /*expectedUserEmail=*/base::SysUTF8ToNSString(kTestEmail));
   [identity_interaction_manager_ simulateDidTapAddAccount];
   WaitForFakeAddAccountViewDismissed();
 }
@@ -217,11 +216,37 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentReauthPrimaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(
+      /*expectedUserEmail=*/base::SysUTF8ToNSString(kTestEmail));
   [identity_interaction_manager_ simulateDidTapCancel];
   WaitForFakeAddAccountViewDismissed();
 }
 
+// Verifies the following state in the successful reauth flow:
+//   - No last know sync account in the identity service
+//   - Completion callback is called with success state
+//
+// Regression test for crbug/1443096
+TEST_F(AddAccountSigninManagerTest,
+       ReauthIntentWithSuccessNoLastKnowSyncAccount) {
+  PrefService* prefs = browser_state_->GetPrefs();
+  prefs->ClearPref(prefs::kGoogleServicesLastUsername);
+  prefs->ClearPref(prefs::kGoogleServicesLastGaiaId);
+
+  // Verify that completion was called with canceled result state.
+  FakeSystemIdentityInteractionManager.identity = fake_identity_;
+  OCMExpect([signin_manager_delegate_
+      addAccountSigninManagerFinishedWithSigninResult:
+          SigninCoordinatorResultSuccess
+                                             identity:fake_identity_]);
+
+  [signin_manager_
+      showSigninWithIntent:AddAccountSigninIntentReauthPrimaryAccount];
+  WaitForFakeAddAccountViewPresented(/*expectedUserEmail=*/nil);
+  [identity_interaction_manager_ simulateDidTapAddAccount];
+  WaitForFakeAddAccountViewDismissed();
+}
+
 // Verifies the following state in the reauth flow with an error handled by the
 // view controller:
 //   - Account is not added to the identity service
@@ -235,7 +260,8 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentReauthPrimaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(
+      /*expectedUserEmail=*/base::SysUTF8ToNSString(kTestEmail));
   [identity_interaction_manager_ simulateDidThrowUnhandledError];
   WaitForFakeAddAccountViewDismissed();
 }
@@ -249,7 +275,8 @@
 
   [signin_manager_
       showSigninWithIntent:AddAccountSigninIntentReauthPrimaryAccount];
-  WaitForFakeAddAccountViewPresented();
+  WaitForFakeAddAccountViewPresented(
+      /*expectedUserEmail=*/base::SysUTF8ToNSString(kTestEmail));
   __block BOOL completionCalled = NO;
   [signin_manager_ interruptAddAccountAnimated:YES
                                     completion:^() {
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index f06d5a0..65cbebfa 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -153,15 +153,6 @@
   ]
 }
 
-source_set("autofill_message") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "save_card_message_with_links.h",
-    "save_card_message_with_links.mm",
-  ]
-  deps = [ "//url:url" ]
-}
-
 source_set("autofill_metrics") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.h b/ios/chrome/browser/ui/badges/badge_mediator.h
index ca89e4d..0e74186 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator.h
+++ b/ios/chrome/browser/ui/badges/badge_mediator.h
@@ -10,14 +10,18 @@
 #import "ios/chrome/browser/ui/badges/badge_delegate.h"
 
 @protocol BadgeConsumer;
-@protocol BadgeItem;
-class Browser;
 @protocol BrowserCoordinatorCommands;
+class OverlayPresenter;
+class WebStateList;
 
 // A mediator object that updates the consumer when the state of badges changes.
 @interface BadgeMediator : NSObject <BadgeDelegate>
 
-- (instancetype)initWithBrowser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithWebStateList:(WebStateList*)webStateList
+                    overlayPresenter:(OverlayPresenter*)overlayPresenter
+                         isIncognito:(BOOL)isIncognito
+    NS_DESIGNATED_INITIALIZER;
+
 - (instancetype)init NS_UNAVAILABLE;
 
 // Stops observing all objects.
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.mm b/ios/chrome/browser/ui/badges/badge_mediator.mm
index 95ea7a3a..0bed93f 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator.mm
@@ -21,8 +21,6 @@
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h"
 #import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
@@ -87,23 +85,23 @@
 
 @implementation BadgeMediator
 
-- (instancetype)initWithBrowser:(Browser*)browser {
+- (instancetype)initWithWebStateList:(WebStateList*)webStateList
+                    overlayPresenter:(OverlayPresenter*)overlayPresenter
+                         isIncognito:(BOOL)isIncognito {
   self = [super init];
   if (self) {
-    DCHECK(browser);
     // Create the incognito badge if `browser` is off-the-record.
-    if (browser->GetBrowserState()->IsOffTheRecord()) {
+    if (isIncognito) {
       _offTheRecordBadge =
           [[BadgeStaticItem alloc] initWithBadgeType:kBadgeTypeIncognito];
     }
     // Set up the OverlayPresenterObserver for the infobar banner presentation.
     _overlayPresenterObserver =
         std::make_unique<OverlayPresenterObserverBridge>(self);
-    _overlayPresenter =
-        OverlayPresenter::FromBrowser(browser, OverlayModality::kInfobarBanner);
+    _overlayPresenter = overlayPresenter;
     _overlayPresenter->AddObserver(_overlayPresenterObserver.get());
     // Set up the WebStateList and its observer.
-    _webStateList = browser->GetWebStateList();
+    _webStateList = webStateList;
     _webState = _webStateList->GetActiveWebState();
 
     _webStateListObserver = std::make_unique<WebStateListObserverBridge>(self);
diff --git a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
index cbc1610..de53124 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
@@ -84,15 +84,18 @@
       : badge_consumer_([[FakeBadgeConsumer alloc] init]),
         browser_state_(TestChromeBrowserState::Builder().Build()),
         browser_(std::make_unique<TestBrowser>(browser_state())) {
-    OverlayPresenter::FromBrowser(browser(), OverlayModality::kInfobarBanner)
-        ->SetPresentationContext(&overlay_presentation_context_);
-    badge_mediator_ = [[BadgeMediator alloc] initWithBrowser:browser()];
+    overlay_presenter_ = OverlayPresenter::FromBrowser(
+        browser(), OverlayModality::kInfobarBanner);
+    overlay_presenter_->SetPresentationContext(&overlay_presentation_context_);
+    badge_mediator_ =
+        [[BadgeMediator alloc] initWithWebStateList:web_state_list()
+                                   overlayPresenter:overlay_presenter_
+                                        isIncognito:is_off_the_record()];
     badge_mediator_.consumer = badge_consumer_;
   }
 
   ~BadgeMediatorTest() override {
-    OverlayPresenter::FromBrowser(browser(), OverlayModality::kInfobarBanner)
-        ->SetPresentationContext(nullptr);
+    overlay_presenter_->SetPresentationContext(nullptr);
     [badge_mediator_ disconnect];
   }
 
@@ -155,6 +158,7 @@
   std::unique_ptr<Browser> browser_;
   FakeOverlayPresentationContext overlay_presentation_context_;
   BadgeMediator* badge_mediator_ = nil;
+  OverlayPresenter* overlay_presenter_ = nullptr;
 };
 
 // Test that the BadgeMediator responds with no displayed and fullscreen badge
@@ -268,7 +272,10 @@
   badge_consumer_ = nil;
 
   badge_consumer_ = [[FakeBadgeConsumer alloc] init];
-  badge_mediator_ = [[BadgeMediator alloc] initWithBrowser:browser()];
+  badge_mediator_ =
+      [[BadgeMediator alloc] initWithWebStateList:web_state_list()
+                                 overlayPresenter:overlay_presenter_
+                                      isIncognito:is_off_the_record()];
   badge_mediator_.consumer = badge_consumer_;
   ASSERT_TRUE(badge_consumer_.displayedBadge);
   EXPECT_EQ(badge_consumer_.displayedBadge.badgeType, kBadgeTypePasswordSave);
diff --git a/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mediator.mm b/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mediator.mm
index ea5ba7ed..3f9bd2cb 100644
--- a/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mediator.mm
+++ b/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mediator.mm
@@ -175,20 +175,30 @@
 }
 
 - (void)bookmarkModel:(bookmarks::BookmarkModel*)model
-        didDeleteNode:(const bookmarks::BookmarkNode*)node
+       willDeleteNode:(const bookmarks::BookmarkNode*)node
            fromFolder:(const bookmarks::BookmarkNode*)folder {
   if (self.ignoresBookmarkModelChanges) {
     return;
   }
 
-  if (self.bookmark == node) {
+  if (self.bookmark->HasAncestor(node)) {
     _bookmark = nullptr;
     [self.delegate bookmarkEditorMediatorWantsDismissal:self];
-  } else if (self.folder == node) {
-    [self changeFolder:self.bookmarkModel->mobile_node()];
+  } else if (self.folder->HasAncestor(node)) {
+    // This might happen when the user has changed `self.folder` but has not
+    // commited the changes by pressing done. And in the background the chosen
+    // folder was deleted.
+    [self changeFolder:model->mobile_node()];
   }
 }
 
+- (void)bookmarkModel:(bookmarks::BookmarkModel*)model
+        didDeleteNode:(const bookmarks::BookmarkNode*)node
+           fromFolder:(const bookmarks::BookmarkNode*)folder {
+  // No-op. Bookmark deletion handled in
+  // `bookmarkModel:willDeleteNode:fromFolder:`
+}
+
 - (void)bookmarkModelRemovedAllNodes:(bookmarks::BookmarkModel*)model {
   CHECK(!self.ignoresBookmarkModelChanges);
   _bookmark = nullptr;
@@ -248,7 +258,7 @@
     [self.delegate
         showSnackbarMessage:bookmark_utils_ios::DeleteBookmarksWithUndoToast(
                                 nodes, {[self bookmarkModel]}, _browserState)];
-    _bookmark = nullptr;
+    [self.delegate bookmarkEditorMediatorWantsDismissal:self];
   }
 }
 
diff --git a/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mutator.h b/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mutator.h
index 3e081be..fc9073b 100644
--- a/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mutator.h
+++ b/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_mutator.h
@@ -19,7 +19,8 @@
 // Save the bookmark being edited.
 - (void)commitBookmarkChangesWithURLString:(NSString*)URL name:(NSString*)name;
 
-// Delete the bookmark being edited.
+// Delete the bookmark being edited. This will also dismiss the editor UI
+// afterwards.
 - (void)deleteBookmark;
 
 // TODO(crbug.com/1404311): Remove those accessor and setters.
diff --git a/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_view_controller.mm b/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_view_controller.mm
index 48bc44b9..91b46d1 100644
--- a/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/editor/bookmarks_editor_view_controller.mm
@@ -323,7 +323,6 @@
   base::RecordAction(
       base::UserMetricsAction("MobileBookmarksEditorDeletedBookmark"));
   [self.mutator deleteBookmark];
-  [self.delegate bookmarkEditorWantsDismissal:self];
 }
 
 - (void)moveBookmark {
diff --git a/ios/chrome/browser/ui/bookmarks/folder_editor/bookmarks_folder_editor_view_controller.mm b/ios/chrome/browser/ui/bookmarks/folder_editor/bookmarks_folder_editor_view_controller.mm
index 64048d6..56424299 100644
--- a/ios/chrome/browser/ui/bookmarks/folder_editor/bookmarks_folder_editor_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/folder_editor/bookmarks_folder_editor_view_controller.mm
@@ -405,19 +405,26 @@
   }
 }
 
+- (void)bookmarkModel:(bookmarks::BookmarkModel*)model
+       willDeleteNode:(const bookmarks::BookmarkNode*)node
+           fromFolder:(const bookmarks::BookmarkNode*)folder {
+  if (_folder->HasAncestor(node)) {
+    _folder = nullptr;
+    [self dismiss];
+  } else if (_parentFolder->HasAncestor(node)) {
+    // This might happen when the user has changed `_parentFolder` but has not
+    // commited the changes by pressing done. And in the background the chosen
+    // folder was deleted.
+    _parentFolder = model->mobile_node();
+    [self updateParentFolderState];
+  }
+}
+
 - (void)bookmarkModel:(BookmarkModel*)model
         didDeleteNode:(const BookmarkNode*)node
            fromFolder:(const BookmarkNode*)folder {
-  if (node == _parentFolder) {
-    _parentFolder = NULL;
-    [self updateParentFolderState];
-    return;
-  }
-  if (node == _folder) {
-    _folder = NULL;
-    _editingExistingFolder = NO;
-    [self updateEditingState];
-  }
+  // No-op. Bookmark deletion handled in
+  // `bookmarkModel:willDeleteNode:fromFolder:`
 }
 
 - (void)bookmarkModelRemovedAllNodes:(BookmarkModel*)model {
@@ -425,8 +432,7 @@
     return;  // The current parent folder is still valid.
   }
 
-  _parentFolder = NULL;
-  [self updateParentFolderState];
+  [self dismiss];
 }
 
 #pragma mark - BookmarkTextFieldItemDelegate
diff --git a/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_mediator.mm b/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_mediator.mm
index 08445aa..dec3687 100644
--- a/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_mediator.mm
+++ b/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_mediator.mm
@@ -515,16 +515,21 @@
   }
 }
 
+// `node` will be deleted from `folder`.
+- (void)bookmarkModel:(bookmarks::BookmarkModel*)model
+       willDeleteNode:(const bookmarks::BookmarkNode*)node
+           fromFolder:(const bookmarks::BookmarkNode*)folder {
+  DCHECK(node);
+  if (self.displayedNode && self.displayedNode->HasAncestor(node)) {
+    self.displayedNode = nullptr;
+  }
+}
+
 // `node` was deleted from `folder`.
 - (void)bookmarkModel:(bookmarks::BookmarkModel*)model
         didDeleteNode:(const bookmarks::BookmarkNode*)node
            fromFolder:(const bookmarks::BookmarkNode*)folder {
-  if (self.currentlyShowingSearchResults) {
-    [self.consumer refreshContents];
-  } else if (self.displayedNode == node) {
-    self.displayedNode = NULL;
-    [self.consumer refreshContents];
-  }
+  [self.consumer refreshContents];
 }
 
 // All non-permanent nodes have been removed.
diff --git a/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn b/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn
index d95cf04d..a1e3adf 100644
--- a/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn
+++ b/ios/chrome/browser/ui/incognito_interstitial/BUILD.gn
@@ -50,7 +50,6 @@
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_browser_agent",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
-    "//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
     "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/ntp/incognito",
     "//ios/chrome/browser/ui/ntp/incognito:util",
diff --git a/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator.mm b/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator.mm
index fd267de0..caa6656 100644
--- a/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator.mm
+++ b/ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator.mm
@@ -11,7 +11,6 @@
 #import "ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_view_controller.h"
 #import "ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_view_controller_delegate.h"
-#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
 #import "ios/chrome/browser/ui/ntp/incognito/incognito_view_util.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_url_loader_delegate.h"
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
@@ -70,34 +69,13 @@
 
 - (void)didTapPrimaryActionButton {
   // Dismiss modals (including interstitial) and open link in incognito tab.
-  __weak __typeof(self) weakSelf = self;
-  UrlLoadParams copyOfUrlLoadParams = self.urlLoadParams;
-  void (^dismissModalsAndOpenTab)() = ^{
-    __typeof(self) strongSelf = weakSelf;
-    strongSelf.incognitoInterstitialAction =
-        IncognitoInterstitialActions::kOpenInChromeIncognito;
-    [strongSelf.tabOpener
-        dismissModalsAndMaybeOpenSelectedTabInMode:
-            ApplicationModeForTabOpening::INCOGNITO
-                                 withUrlLoadParams:copyOfUrlLoadParams
-                                    dismissOmnibox:YES
-                                        completion:nil];
-  };
-
-  SceneState* sceneState =
-      SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
-  IncognitoReauthSceneAgent* reauthAgent =
-      [IncognitoReauthSceneAgent agentFromScene:sceneState];
-  if (reauthAgent.authenticationRequired) {
-    [reauthAgent
-        authenticateIncognitoContentWithCompletionBlock:^(BOOL success) {
-          if (success) {
-            dismissModalsAndOpenTab();
-          }
-        }];
-  } else {
-    dismissModalsAndOpenTab();
-  }
+  self.incognitoInterstitialAction =
+      IncognitoInterstitialActions::kOpenInChromeIncognito;
+  [self.tabOpener dismissModalsAndMaybeOpenSelectedTabInMode:
+                      ApplicationModeForTabOpening::INCOGNITO
+                                           withUrlLoadParams:self.urlLoadParams
+                                              dismissOmnibox:YES
+                                                  completion:nil];
 }
 
 - (void)didTapSecondaryActionButton {
diff --git a/ios/chrome/browser/ui/infobars/modals/BUILD.gn b/ios/chrome/browser/ui/infobars/modals/BUILD.gn
index 4da6463..08a416e4 100644
--- a/ios/chrome/browser/ui/infobars/modals/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/modals/BUILD.gn
@@ -33,6 +33,7 @@
     "//components/translate/core/browser",
     "//components/translate/core/common",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/autofill/message",
     "//ios/chrome/browser/infobars:public",
     "//ios/chrome/browser/net:crurl",
     "//ios/chrome/browser/passwords:public",
@@ -40,7 +41,6 @@
     "//ios/chrome/browser/shared/ui/table_view",
     "//ios/chrome/browser/shared/ui/table_view:styler",
     "//ios/chrome/browser/shared/ui/util",
-    "//ios/chrome/browser/ui/autofill:autofill_message",
     "//ios/chrome/browser/ui/autofill:autofill_metrics",
     "//ios/chrome/browser/ui/autofill/cells",
     "//ios/chrome/browser/ui/infobars/coordinators:translate_public",
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
index 54c675ee..9274f38d 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
@@ -9,6 +9,7 @@
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
 #import "components/autofill/core/common/autofill_features.h"
+#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
 #import "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
 #import "ios/chrome/browser/net/crurl.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_button_item.h"
@@ -18,7 +19,6 @@
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/autofill/cells/target_account_item.h"
 #import "ios/chrome/browser/ui/autofill/save_card_infobar_metrics_recorder.h"
-#import "ios/chrome/browser/ui/autofill/save_card_message_with_links.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_modal_delegate.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 3c2ff470..dd00fb6a 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -213,7 +213,12 @@
   [self.viewController setBadgeView:self.badgeViewController.view];
   [self.badgeViewController didMoveToParentViewController:self.viewController];
   // Create BadgeMediator and set the viewController as its consumer.
-  self.badgeMediator = [[BadgeMediator alloc] initWithBrowser:self.browser];
+  OverlayPresenter* overlayPresenter = OverlayPresenter::FromBrowser(
+      self.browser, OverlayModality::kInfobarBanner);
+  self.badgeMediator =
+      [[BadgeMediator alloc] initWithWebStateList:self.webStateList
+                                 overlayPresenter:overlayPresenter
+                                      isIncognito:isIncognito];
   self.badgeMediator.consumer = self.badgeViewController;
   // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
   // clean up.
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
index 38fa4c3..e24ef58 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
@@ -17,11 +17,11 @@
     "//base",
     "//components/autofill/core/common",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/autofill/message",
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/common/infobars",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/shared/ui/util",
-    "//ios/chrome/browser/ui/autofill:autofill_message",
     "//ios/chrome/browser/ui/infobars/modals",
     "//ios/chrome/browser/ui/overlays:coordinators",
     "//ios/chrome/browser/ui/overlays/infobar_modal:coordinators",
@@ -44,12 +44,12 @@
     "//components/infobars/core",
     "//components/prefs",
     "//components/signin/public/identity_manager",
+    "//ios/chrome/browser/autofill/message",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars/test",
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/overlays/test",
-    "//ios/chrome/browser/ui/autofill:autofill_message",
     "//ios/chrome/browser/ui/infobars/modals",
     "//ios/chrome/browser/ui/infobars/modals/test",
     "//ios/chrome/browser/ui/infobars/test",
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
index 2ceddf7..a860e9b 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
@@ -14,11 +14,11 @@
 #import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
 #import "components/autofill/core/browser/payments/test_legal_message_line.h"
 #import "components/signin/public/identity_manager/account_info.h"
+#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
 #import "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
 #import "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
-#import "ios/chrome/browser/ui/autofill/save_card_message_with_links.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_modal_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_delegate.h"
 #import "testing/gtest_mac.h"
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h
index e1229d93..06d2993 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h
@@ -10,7 +10,7 @@
 #import "ios/chrome/browser/ui/commerce/price_card/price_card_view.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_theme.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_cell.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.h"
 
 @class GridCell;
 
@@ -64,7 +64,7 @@
 - (void)hideActivityIndicator;
 @end
 
-@interface GridTransitionCell : GridCell <GridToTabTransitionView>
+@interface GridTransitionCell : GridCell <LegacyGridToTabTransitionView>
 // Returns a cell with the same theme, icon, snapshot, title, and frame as
 // `cell` (but no delegate or identifier) for use in animated transitions.
 + (instancetype)transitionCellFromCell:(GridCell*)cell;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
index 0dcd39f..fd48210 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
@@ -19,7 +19,7 @@
 @protocol TabCollectionDragDropHandler;
 @protocol GridEmptyView;
 @protocol GridShareableItemsProvider;
-@class GridTransitionLayout;
+@class LegacyGridTransitionLayout;
 @class GridViewController;
 @protocol IncognitoReauthCommands;
 @protocol PriceCardDataSource;
@@ -164,7 +164,7 @@
 @property(nonatomic, assign) CGFloat notSelectedTabCellOpacity;
 
 // Returns the layout of the grid for use in an animated transition.
-- (GridTransitionLayout*)transitionLayout;
+- (LegacyGridTransitionLayout*)transitionLayout;
 
 // Notifies the ViewController that its content might soon be displayed.
 - (void)prepareForAppearance;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
index 9dd3a05..9969d8c 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
@@ -47,7 +47,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/suggested_actions/suggested_actions_grid_cell.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/suggested_actions/suggested_actions_view_controller.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_context_menu_provider.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/grit/ios_strings.h"
@@ -470,11 +470,12 @@
       containsObject:selectedIndexPath];
 }
 
-- (GridTransitionLayout*)transitionLayout {
+- (LegacyGridTransitionLayout*)transitionLayout {
   [self.collectionView layoutIfNeeded];
-  NSMutableArray<GridTransitionItem*>* items = [[NSMutableArray alloc] init];
-  GridTransitionActiveItem* activeItem;
-  GridTransitionItem* selectionItem;
+  NSMutableArray<LegacyGridTransitionItem*>* items =
+      [[NSMutableArray alloc] init];
+  LegacyGridTransitionActiveItem* activeItem;
+  LegacyGridTransitionItem* selectionItem;
   for (NSIndexPath* path in self.collectionView.indexPathsForVisibleItems) {
     if (path.section != kOpenTabsSectionIndex)
       continue;
@@ -489,27 +490,28 @@
     if ([cell hasIdentifier:self.selectedItemID]) {
       GridTransitionCell* activeCell =
           [GridTransitionCell transitionCellFromCell:cell];
-      activeItem = [GridTransitionActiveItem itemWithCell:activeCell
-                                                   center:attributes.center
-                                                     size:attributes.size];
+      activeItem =
+          [LegacyGridTransitionActiveItem itemWithCell:activeCell
+                                                center:attributes.center
+                                                  size:attributes.size];
       // If the active item is the last inserted item, it needs to be animated
       // differently.
       if ([cell hasIdentifier:self.lastInsertedItemID])
         activeItem.isAppearing = YES;
-      selectionItem = [GridTransitionItem
+      selectionItem = [LegacyGridTransitionItem
           itemWithCell:[GridCell transitionSelectionCellFromCell:cell]
                 center:attributes.center];
     } else {
       UIView* cellSnapshot = [cell snapshotViewAfterScreenUpdates:YES];
-      GridTransitionItem* item =
-          [GridTransitionItem itemWithCell:cellSnapshot
-                                    center:attributes.center];
+      LegacyGridTransitionItem* item =
+          [LegacyGridTransitionItem itemWithCell:cellSnapshot
+                                          center:attributes.center];
       [items addObject:item];
     }
   }
-  return [GridTransitionLayout layoutWithInactiveItems:items
-                                            activeItem:activeItem
-                                         selectionItem:selectionItem];
+  return [LegacyGridTransitionLayout layoutWithInactiveItems:items
+                                                  activeItem:activeItem
+                                               selectionItem:selectionItem];
 }
 
 - (void)prepareForAppearance {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_cell.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_cell.h
index 491f7a65..4a36b83 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_cell.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_cell.h
@@ -8,7 +8,7 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_cell.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.h"
 
 // A cell for the pinned tabs view. Contains an icon, title, snapshot.
 @interface PinnedCell : TabCell
@@ -45,7 +45,7 @@
 //
 // TODO(crbug.com/1412115): Refactor `Transition` cells into separate header
 // and implementation files.
-@interface PinnedTransitionCell : PinnedCell <GridToTabTransitionView>
+@interface PinnedTransitionCell : PinnedCell <LegacyGridToTabTransitionView>
 
 // Returns a cell with the same theme, icon, snapshot, title, and frame as
 // `cell` (but no delegate or identifier) for use in animated transitions.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.h
index 63fe009b..e4b26b6 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.h
@@ -9,7 +9,7 @@
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_collection_consumer.h"
 
-@class GridTransitionLayout;
+@class LegacyGridTransitionLayout;
 @class PinnedTabsViewController;
 @protocol TabCollectionDragDropHandler;
 @protocol TabContextMenuProvider;
@@ -95,7 +95,7 @@
 - (void)dropAnimationDidEnd;
 
 // Returns the layout of the pinned tabs to be used in an animated transition.
-- (GridTransitionLayout*)transitionLayout;
+- (LegacyGridTransitionLayout*)transitionLayout;
 
 // Returns whether there is a selected cell in the collection.
 - (BOOL)hasSelectedCell;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.mm
index ee9a21f8..9b2db06 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_view_controller.mm
@@ -19,7 +19,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_constants.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_context_menu_provider.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
@@ -210,11 +210,11 @@
   [self dragSessionEnabled:NO];
 }
 
-- (GridTransitionLayout*)transitionLayout {
+- (LegacyGridTransitionLayout*)transitionLayout {
   [self.collectionView layoutIfNeeded];
 
-  GridTransitionActiveItem* activeItem;
-  GridTransitionItem* selectionItem;
+  LegacyGridTransitionActiveItem* activeItem;
+  LegacyGridTransitionItem* selectionItem;
 
   NSIndexPath* selectedItemIndexPath =
       self.collectionView.indexPathsForSelectedItems.firstObject;
@@ -231,23 +231,23 @@
 
     PinnedTransitionCell* activeCell =
         [PinnedTransitionCell transitionCellFromCell:selectedCell];
-    activeItem = [GridTransitionActiveItem itemWithCell:activeCell
-                                                 center:attributes.center
-                                                   size:attributes.size];
+    activeItem = [LegacyGridTransitionActiveItem itemWithCell:activeCell
+                                                       center:attributes.center
+                                                         size:attributes.size];
     // If the active item is the last inserted item, it needs to be animated
     // differently.
     if ([selectedCell hasIdentifier:_lastInsertedItemID]) {
       activeItem.isAppearing = YES;
     }
 
-    selectionItem = [GridTransitionItem
+    selectionItem = [LegacyGridTransitionItem
         itemWithCell:[PinnedCell transitionSelectionCellFromCell:selectedCell]
               center:attributes.center];
   }
 
-  return [GridTransitionLayout layoutWithInactiveItems:@[]
-                                            activeItem:activeItem
-                                         selectionItem:selectionItem];
+  return [LegacyGridTransitionLayout layoutWithInactiveItems:@[]
+                                                  activeItem:activeItem
+                                               selectionItem:selectionItem];
 }
 
 - (BOOL)isCollectionEmpty {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
index fd9fc1be..ce1edbc9 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -52,7 +52,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_page_control.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/thumb_strip_plus_sign_button.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/grit/ios_strings.h"
@@ -461,8 +461,9 @@
   }
 }
 
-- (GridTransitionLayout*)transitionLayout:(TabGridPage)activePage {
-  GridTransitionLayout* layout = [self transitionLayoutForPage:activePage];
+- (LegacyGridTransitionLayout*)transitionLayout:(TabGridPage)activePage {
+  LegacyGridTransitionLayout* layout =
+      [self transitionLayoutForPage:activePage];
   if (!layout) {
     return nil;
   }
@@ -1072,7 +1073,7 @@
 }
 
 // Returns transition layout for the provided `page`.
-- (GridTransitionLayout*)transitionLayoutForPage:(TabGridPage)page {
+- (LegacyGridTransitionLayout*)transitionLayoutForPage:(TabGridPage)page {
   switch (page) {
     case TabGridPageIncognitoTabs:
       return [self.incognitoTabsViewController transitionLayout];
@@ -1084,12 +1085,12 @@
 }
 
 // Returns transition layout provider for the regular tabs page.
-- (GridTransitionLayout*)transitionLayoutForRegularTabsPage {
-  GridTransitionLayout* regularTabsTransitionLayout =
+- (LegacyGridTransitionLayout*)transitionLayoutForRegularTabsPage {
+  LegacyGridTransitionLayout* regularTabsTransitionLayout =
       [self.regularTabsViewController transitionLayout];
 
   if (IsPinnedTabsEnabled()) {
-    GridTransitionLayout* pinnedTabsTransitionLayout =
+    LegacyGridTransitionLayout* pinnedTabsTransitionLayout =
         [self.pinnedTabsViewController transitionLayout];
 
     return [self combineTransitionLayout:regularTabsTransitionLayout
@@ -1103,43 +1104,47 @@
 // priority over `secondaryLayout`. This means that in case there are two
 // activeItems and/or two selectionItems available, only the ones from
 // `primaryLayout` would be picked for a combined layout.
-- (GridTransitionLayout*)
-    combineTransitionLayout:(GridTransitionLayout*)primaryLayout
-       withTransitionLayout:(GridTransitionLayout*)secondaryLayout {
-  NSArray<GridTransitionItem*>* primaryInactiveItems =
+- (LegacyGridTransitionLayout*)
+    combineTransitionLayout:(LegacyGridTransitionLayout*)primaryLayout
+       withTransitionLayout:(LegacyGridTransitionLayout*)secondaryLayout {
+  NSArray<LegacyGridTransitionItem*>* primaryInactiveItems =
       primaryLayout.inactiveItems;
-  NSArray<GridTransitionItem*>* secondaryInactiveItems =
+  NSArray<LegacyGridTransitionItem*>* secondaryInactiveItems =
       secondaryLayout.inactiveItems;
 
-  NSArray<GridTransitionItem*>* inactiveItems =
+  NSArray<LegacyGridTransitionItem*>* inactiveItems =
       [self combineInactiveItems:primaryInactiveItems
                withInactiveItems:secondaryInactiveItems];
 
-  GridTransitionActiveItem* primaryActiveItem = primaryLayout.activeItem;
-  GridTransitionActiveItem* secondaryActiveItem = secondaryLayout.activeItem;
+  LegacyGridTransitionActiveItem* primaryActiveItem = primaryLayout.activeItem;
+  LegacyGridTransitionActiveItem* secondaryActiveItem =
+      secondaryLayout.activeItem;
 
   // Prefer primary active item.
-  GridTransitionActiveItem* activeItem =
+  LegacyGridTransitionActiveItem* activeItem =
       primaryActiveItem ? primaryActiveItem : secondaryActiveItem;
 
-  GridTransitionItem* primarySelectionItem = primaryLayout.selectionItem;
-  GridTransitionItem* secondarySelectionItem = secondaryLayout.selectionItem;
+  LegacyGridTransitionItem* primarySelectionItem = primaryLayout.selectionItem;
+  LegacyGridTransitionItem* secondarySelectionItem =
+      secondaryLayout.selectionItem;
 
   // Prefer primary selection item.
-  GridTransitionItem* selectionItem =
+  LegacyGridTransitionItem* selectionItem =
       primarySelectionItem ? primarySelectionItem : secondarySelectionItem;
 
-  return [GridTransitionLayout layoutWithInactiveItems:inactiveItems
-                                            activeItem:activeItem
-                                         selectionItem:selectionItem];
+  return [LegacyGridTransitionLayout layoutWithInactiveItems:inactiveItems
+                                                  activeItem:activeItem
+                                               selectionItem:selectionItem];
 }
 
 // Combines two arrays of inactive items into one. The `primaryInactiveItems`
 // (if any) would be placed in the front of the resulting array, whether the
 // `secondaryInactiveItems` would be placed in the back.
-- (NSArray<GridTransitionItem*>*)
-    combineInactiveItems:(NSArray<GridTransitionItem*>*)primaryInactiveItems
-       withInactiveItems:(NSArray<GridTransitionItem*>*)secondaryInactiveItems {
+- (NSArray<LegacyGridTransitionItem*>*)
+    combineInactiveItems:
+        (NSArray<LegacyGridTransitionItem*>*)primaryInactiveItems
+       withInactiveItems:
+           (NSArray<LegacyGridTransitionItem*>*)secondaryInactiveItems {
   if (primaryInactiveItems == nil) {
     primaryInactiveItems = @[];
   }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/BUILD.gn
index eafdd12..f77ca4d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/BUILD.gn
@@ -6,12 +6,12 @@
 
 source_set("transitions") {
   sources = [
-    "grid_to_tab_transition_view.h",
-    "grid_transition_layout.h",
-    "grid_transition_layout.mm",
+    "legacy_grid_to_tab_transition_view.h",
     "legacy_grid_transition_animation.h",
     "legacy_grid_transition_animation.mm",
     "legacy_grid_transition_animation_layout_providing.h",
+    "legacy_grid_transition_layout.h",
+    "legacy_grid_transition_layout.mm",
     "legacy_tab_grid_transition_handler.h",
     "legacy_tab_grid_transition_handler.mm",
   ]
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.h
similarity index 91%
rename from ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h
rename to ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.h
index de9cc6c..7e24c3f88 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.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_TAB_SWITCHER_TAB_GRID_TRANSITIONS_GRID_TO_TAB_TRANSITION_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_GRID_TO_TAB_TRANSITION_VIEW_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_LEGACY_GRID_TO_TAB_TRANSITION_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_LEGACY_GRID_TO_TAB_TRANSITION_VIEW_H_
 
 #import <UIKit/UIKit.h>
 
@@ -11,7 +11,7 @@
 
 // An collection of properties and methods a view must support in order to be
 // used to animate the transition between a grid cell and a browser tab.
-@protocol GridToTabTransitionView
+@protocol LegacyGridToTabTransitionView
 
 // The subview at the top of the view in 'cell' state.
 @property(nonatomic, strong) UIView* topCellView;
@@ -46,4 +46,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_GRID_TO_TAB_TRANSITION_VIEW_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_LEGACY_GRID_TO_TAB_TRANSITION_VIEW_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h
index bc5db34..d8ba0474 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h
@@ -7,7 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
-@class GridTransitionLayout;
+@class LegacyGridTransitionLayout;
 
 // The directions the animation can take.
 typedef NS_ENUM(NSUInteger, GridAnimationDirection) {
@@ -45,7 +45,7 @@
 // the layout the animation should animate to. `delegate` is an object that will
 // be informed about events in this object's animation. `direction` is the
 // direction that the transition will animate.
-- (instancetype)initWithLayout:(GridTransitionLayout*)layout
+- (instancetype)initWithLayout:(LegacyGridTransitionLayout*)layout
                       duration:(NSTimeInterval)duration
                      direction:(GridAnimationDirection)direction
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.mm
index 9d2f8e82..dada22c 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.mm
@@ -5,8 +5,8 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h"
 
 #import "ios/chrome/browser/shared/ui/util/property_animator_group.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h"
 #import "ios/chrome/common/ui/util/ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -64,7 +64,7 @@
 // Based on the above, the correction formula should be:
 //   correction = ((1 - value) - 0.7) * 0.37
 //
-CGFloat CalculateResizeDampingCorrection(GridTransitionLayout* layout) {
+CGFloat CalculateResizeDampingCorrection(LegacyGridTransitionLayout* layout) {
   CGFloat resizeRatio = CGRectGetHeight(layout.activeItem.cell.frame) /
                         CGRectGetHeight(layout.expandedRect);
 
@@ -76,7 +76,7 @@
 // The property animator group backing the public `animator` property.
 @property(nonatomic, readonly) PropertyAnimatorGroup* animations;
 // The layout of the grid for this animation.
-@property(nonatomic, strong) GridTransitionLayout* layout;
+@property(nonatomic, strong) LegacyGridTransitionLayout* layout;
 // The duration of the animation.
 @property(nonatomic, readonly, assign) NSTimeInterval duration;
 // The direction this animation is in.
@@ -90,7 +90,7 @@
 
 @implementation LegacyGridTransitionAnimation
 
-- (instancetype)initWithLayout:(GridTransitionLayout*)layout
+- (instancetype)initWithLayout:(LegacyGridTransitionLayout*)layout
                       duration:(NSTimeInterval)duration
                      direction:(GridAnimationDirection)direction {
   if (self = [super initWithFrame:CGRectZero]) {
@@ -185,7 +185,8 @@
   //
   // (Changing the timing constants above will change the timing % values)
 
-  UIView<GridToTabTransitionView>* activeCell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* activeCell =
+      self.layout.activeItem.cell;
   // The final cell snapshot exactly matches the main tab view of the cell, so
   // it can have an alpha of 0 for the whole animation.
   activeCell.mainTabView.alpha = 0.0;
@@ -217,7 +218,7 @@
   [self.animations addAnimator:fadeInAuxillary];
 
   // C: Round the corners of the active cell.
-  UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* cell = self.layout.activeItem.cell;
   cell.cornerRadius = DeviceCornerRadius();
   auto roundCornersAnimation = ^{
     cell.cornerRadius = self.finalActiveCellCornerRadius;
@@ -240,7 +241,7 @@
   // Additional animations for multiple cells.
   // D: Scale up inactive cells.
   auto scaleUpCellsAnimation = ^{
-    for (GridTransitionItem* item in self.layout.inactiveItems) {
+    for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
       item.cell.transform = CGAffineTransformIdentity;
     }
   };
@@ -257,7 +258,7 @@
 
   // E: Fade in inactive cells.
   auto fadeInCellsAnimation = ^{
-    for (GridTransitionItem* item in self.layout.inactiveItems) {
+    for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
       item.cell.alpha = 1.0;
     }
   };
@@ -319,7 +320,8 @@
   // prevents an abrupt jump when the transition ends and the "real" tab content
   // is shown.
 
-  UIView<GridToTabTransitionView>* activeCell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* activeCell =
+      self.layout.activeItem.cell;
   // The top tab view starts at zero alpha but is crossfaded in.
   activeCell.topTabView.alpha = 0.0;
   // If the active item is appearing, the main tab view is shown. If not, it's
@@ -353,7 +355,7 @@
   [self.animations addAnimator:fadeOutAuxilliary];
 
   // C: Square the active cell's corners.
-  UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* cell = self.layout.activeItem.cell;
   auto squareCornersAnimation = ^{
     cell.cornerRadius = DeviceCornerRadius();
   };
@@ -397,7 +399,7 @@
   // Additional animations for multiple cells.
   // E: Scale down inactive cells.
   auto scaleDownCellsAnimation = ^{
-    for (GridTransitionItem* item in self.layout.inactiveItems) {
+    for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
       item.cell.transform = CGAffineTransformScale(
           item.cell.transform, kInactiveItemScale, kInactiveItemScale);
     }
@@ -410,7 +412,7 @@
 
   // F: Fade out inactive cells.
   auto fadeOutCellsAnimation = ^{
-    for (GridTransitionItem* item in self.layout.inactiveItems) {
+    for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
       item.cell.alpha = 0.0;
     }
   };
@@ -431,7 +433,7 @@
   // Add the selection item first, so it's under ther other views.
   [self addSubview:self.layout.selectionItem.cell];
 
-  for (GridTransitionItem* item in self.layout.inactiveItems) {
+  for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
     [self addSubview:item.cell];
   }
 
@@ -441,14 +443,14 @@
 
 // Prepares the the views for a transition.
 - (void)prepareForTransition {
-  UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* cell = self.layout.activeItem.cell;
   [cell prepareForTransitionWithAnimationDirection:self.direction];
 }
 
 // Positions the active item in the expanded grid position with a zero corner
 // radius and a 0% opacity auxilliary view.
 - (void)positionExpandedActiveItem {
-  UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* cell = self.layout.activeItem.cell;
   cell.frame = self.layout.expandedRect;
   [cell positionTabViews];
 }
@@ -456,7 +458,7 @@
 // Positions all of the inactive items in their grid positions.
 // Fades and scales each of those items.
 - (void)prepareInactiveItemsForAppearance {
-  for (GridTransitionItem* item in self.layout.inactiveItems) {
+  for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
     [self positionItemInGrid:item];
     item.cell.alpha = 0.2;
     item.cell.transform = CGAffineTransformScale(
@@ -468,7 +470,7 @@
 // Positions the active item in the regular grid position with its final
 // corner radius.
 - (void)positionAndScaleActiveItemInGrid {
-  UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
+  UIView<LegacyGridToTabTransitionView>* cell = self.layout.activeItem.cell;
   cell.transform = CGAffineTransformIdentity;
   CGRect frame = cell.frame;
   frame.size = self.layout.activeItem.size;
@@ -479,7 +481,7 @@
 
 // Prepares all of the items for an expansion animation.
 - (void)prepareAllItemsForExpansion {
-  for (GridTransitionItem* item in self.layout.inactiveItems) {
+  for (LegacyGridTransitionItem* item in self.layout.inactiveItems) {
     [self positionItemInGrid:item];
   }
   [self positionItemInGrid:self.layout.activeItem];
@@ -488,7 +490,7 @@
 }
 
 // Positions `item` in it grid position.
-- (void)positionItemInGrid:(GridTransitionItem*)item {
+- (void)positionItemInGrid:(LegacyGridTransitionItem*)item {
   UIView* cell = item.cell;
   CGPoint newCenter = [self.superview convertPoint:item.center fromView:nil];
   cell.center = newCenter;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation_layout_providing.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation_layout_providing.h
index a726379b..d454946 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation_layout_providing.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation_layout_providing.h
@@ -10,7 +10,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_paging.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h"
 
-@class GridTransitionLayout;
+@class LegacyGridTransitionLayout;
 
 // Objects conforming to this protocol can provide information for the
 // animation of the transitions from and to a grid.
@@ -27,7 +27,7 @@
 
 // Asks the provider for the layout of the grid for the `activePage`, used in
 // transition animations.
-- (GridTransitionLayout*)transitionLayout:(TabGridPage)activePage;
+- (LegacyGridTransitionLayout*)transitionLayout:(TabGridPage)activePage;
 
 // Asks the provider for the view to which the animation views should be added.
 - (UIView*)animationViewsContainer;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h
similarity index 72%
rename from ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h
rename to ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h
index d4fe5cf..98f59333 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h
@@ -2,32 +2,33 @@
 // 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_TAB_SWITCHER_TAB_GRID_TRANSITIONS_GRID_TRANSITION_LAYOUT_H_
-#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_GRID_TRANSITION_LAYOUT_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_LEGACY_GRID_TRANSITION_LAYOUT_H_
+#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_LEGACY_GRID_TRANSITION_LAYOUT_H_
 
 #import <UIKit/UIKit.h>
 
-@protocol GridToTabTransitionView;
-@class GridTransitionActiveItem;
-@class GridTransitionItem;
+@protocol LegacyGridToTabTransitionView;
+@class LegacyGridTransitionActiveItem;
+@class LegacyGridTransitionItem;
 
 // An encapsulation of information for the layout of a grid of cells that will
 // be used in an animated transition. The layout object is composed of layout
 // items (see below).
-@interface GridTransitionLayout : NSObject
+@interface LegacyGridTransitionLayout : NSObject
 
 // The inactive items in the layout. `activeItem` and `selectionItem` are not
 // in this array.
 @property(nonatomic, copy, readonly)
-    NSArray<GridTransitionItem*>* inactiveItems;
+    NSArray<LegacyGridTransitionItem*>* inactiveItems;
 
 // The item in the layout (if any) that's the 'active' item (the one that will
 // expand and contract).
-@property(nonatomic, strong, readonly) GridTransitionActiveItem* activeItem;
+@property(nonatomic, strong, readonly)
+    LegacyGridTransitionActiveItem* activeItem;
 
 // An item that shows the selections state (and nothing else) of the active
 // item.
-@property(nonatomic, strong, readonly) GridTransitionItem* selectionItem;
+@property(nonatomic, strong, readonly) LegacyGridTransitionItem* selectionItem;
 
 // The rect, in UIWindow coordinates, that an "expanded" item should occupy.
 @property(nonatomic, assign) CGRect expandedRect;
@@ -39,14 +40,15 @@
 // Creates a new layout object.
 // `inactiveItems` should be non-nil, but it may be empty.
 // `activeItem` and `selectionItem` may be nil.
-+ (instancetype)layoutWithInactiveItems:(NSArray<GridTransitionItem*>*)items
-                             activeItem:(GridTransitionActiveItem*)activeItem
-                          selectionItem:(GridTransitionItem*)selectionItem;
++ (instancetype)
+    layoutWithInactiveItems:(NSArray<LegacyGridTransitionItem*>*)items
+                 activeItem:(LegacyGridTransitionActiveItem*)activeItem
+              selectionItem:(LegacyGridTransitionItem*)selectionItem;
 
 @end
 
 // An encapsulation of information for the layout of a single grid cell.
-@interface GridTransitionItem : NSObject
+@interface LegacyGridTransitionItem : NSObject
 
 // A view with the desired appearance of the cell for animation. This should
 // not be in any view hierarchy when the layout item is created. It should
@@ -63,14 +65,15 @@
 
 @end
 
-// An extension of GridTransitionItem for an item that will transition between
-// 'cell' and 'tab' appearance during the animation.
-@interface GridTransitionActiveItem : GridTransitionItem
+// An extension of LegacyGridTransitionItem for an item that will transition
+// between 'cell' and 'tab' appearance during the animation.
+@interface LegacyGridTransitionActiveItem : LegacyGridTransitionItem
 
 // A view with the desired appearance of the cell for animation. This should
 // not be in any view hierarchy when the layout item is created. It should
 // otherwise be sized correctly and have the correct appearance.
-@property(nonatomic, strong, readonly) UIView<GridToTabTransitionView>* cell;
+@property(nonatomic, strong, readonly)
+    UIView<LegacyGridToTabTransitionView>* cell;
 
 // The size of `cell` in the grid.
 @property(nonatomic, readonly) CGSize size;
@@ -79,7 +82,7 @@
 @property(nonatomic, assign) BOOL isAppearing;
 
 // Creates a new active item instance with `cell`, `center` and `size`.
-+ (instancetype)itemWithCell:(UIView<GridToTabTransitionView>*)cell
++ (instancetype)itemWithCell:(UIView<LegacyGridToTabTransitionView>*)cell
                       center:(CGPoint)center
                         size:(CGSize)size;
 
@@ -90,4 +93,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_GRID_TRANSITION_LAYOUT_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TRANSITIONS_LEGACY_GRID_TRANSITION_LAYOUT_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.mm
similarity index 69%
rename from ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.mm
rename to ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.mm
index 97f2f4b..218802b 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.mm
@@ -2,9 +2,9 @@
 // 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/tab_switcher/tab_grid/transitions/grid_transition_layout.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h"
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_to_tab_transition_view.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_to_tab_transition_view.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -12,24 +12,27 @@
 
 #import "base/check.h"
 
-@interface GridTransitionLayout ()
-@property(nonatomic, readwrite) NSArray<GridTransitionItem*>* inactiveItems;
-@property(nonatomic, readwrite) GridTransitionActiveItem* activeItem;
-@property(nonatomic, readwrite) GridTransitionItem* selectionItem;
+@interface LegacyGridTransitionLayout ()
+@property(nonatomic, readwrite)
+    NSArray<LegacyGridTransitionItem*>* inactiveItems;
+@property(nonatomic, readwrite) LegacyGridTransitionActiveItem* activeItem;
+@property(nonatomic, readwrite) LegacyGridTransitionItem* selectionItem;
 @end
 
-@implementation GridTransitionLayout
+@implementation LegacyGridTransitionLayout
 @synthesize activeItem = _activeItem;
 @synthesize selectionItem = _selectionItem;
 @synthesize inactiveItems = _inactiveItems;
 @synthesize expandedRect = _expandedRect;
 @synthesize frameChanged = _frameChanged;
 
-+ (instancetype)layoutWithInactiveItems:(NSArray<GridTransitionItem*>*)items
-                             activeItem:(GridTransitionActiveItem*)activeItem
-                          selectionItem:(GridTransitionItem*)selectionItem {
++ (instancetype)
+    layoutWithInactiveItems:(NSArray<LegacyGridTransitionItem*>*)items
+                 activeItem:(LegacyGridTransitionActiveItem*)activeItem
+              selectionItem:(LegacyGridTransitionItem*)selectionItem {
   DCHECK(items);
-  GridTransitionLayout* layout = [[GridTransitionLayout alloc] init];
+  LegacyGridTransitionLayout* layout =
+      [[LegacyGridTransitionLayout alloc] init];
   layout.inactiveItems = items;
   layout.activeItem = activeItem;
   layout.selectionItem = selectionItem;
@@ -38,39 +41,39 @@
 
 @end
 
-@interface GridTransitionItem ()
+@interface LegacyGridTransitionItem ()
 @property(nonatomic, readwrite) UIView* cell;
 @property(nonatomic, readwrite) CGPoint center;
 @end
 
-@implementation GridTransitionItem
+@implementation LegacyGridTransitionItem
 @synthesize cell = _cell;
 @synthesize center = _center;
 
 + (instancetype)itemWithCell:(UIView*)cell center:(CGPoint)center {
   DCHECK(cell);
   DCHECK(!cell.superview);
-  GridTransitionItem* item = [[self alloc] init];
+  LegacyGridTransitionItem* item = [[self alloc] init];
   item.cell = cell;
   item.center = center;
   return item;
 }
 @end
 
-@interface GridTransitionActiveItem ()
-@property(nonatomic, readwrite) UIView<GridToTabTransitionView>* cell;
+@interface LegacyGridTransitionActiveItem ()
+@property(nonatomic, readwrite) UIView<LegacyGridToTabTransitionView>* cell;
 @property(nonatomic, readwrite) CGSize size;
 @end
 
-@implementation GridTransitionActiveItem
+@implementation LegacyGridTransitionActiveItem
 @dynamic cell;
 @synthesize size = _size;
 @synthesize isAppearing = _isAppearing;
 
-+ (instancetype)itemWithCell:(UIView<GridToTabTransitionView>*)cell
++ (instancetype)itemWithCell:(UIView<LegacyGridToTabTransitionView>*)cell
                       center:(CGPoint)center
                         size:(CGSize)size {
-  GridTransitionActiveItem* item = [self itemWithCell:cell center:center];
+  LegacyGridTransitionActiveItem* item = [self itemWithCell:cell center:center];
   item.size = size;
   return item;
 }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_tab_grid_transition_handler.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_tab_grid_transition_handler.mm
index a427950f..7e932c0 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_tab_grid_transition_handler.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_tab_grid_transition_handler.mm
@@ -5,9 +5,9 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_tab_grid_transition_handler.h"
 
 #import "ios/chrome/browser/shared/ui/util/named_guide.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/grid_transition_layout.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation_layout_providing.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_layout.h"
 #import "ios/chrome/common/ui/util/ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -200,11 +200,11 @@
 #pragma mark - Private
 
 // Returns the transition layout for the `activePage`, based on the `browser`.
-- (GridTransitionLayout*)transitionLayoutForTabInViewController:
-                             (UIViewController*)viewControllerForTab
-                                                     activePage:(TabGridPage)
-                                                                    activePage {
-  GridTransitionLayout* layout =
+- (LegacyGridTransitionLayout*)
+    transitionLayoutForTabInViewController:
+        (UIViewController*)viewControllerForTab
+                                activePage:(TabGridPage)activePage {
+  LegacyGridTransitionLayout* layout =
       [self.layoutProvider transitionLayout:activePage];
 
   // Get the fram for the snapshotted content of the active tab.
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 ca7567be..720d394ab 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 @@
-3a759e47a7eed319c44afecf260cf1fbc4d7a532
\ No newline at end of file
+21ae963b4192daaebe09e87c2e1bc8fbad92efbb
\ 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 221833b2..93d57a25f 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 @@
-33b3bda89f14a011be0b4e6cdef774ca0322a5c0
\ No newline at end of file
+57d6a3392e5ddd2bd2c68a54fe658950a6f216ea
\ 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 f369e0a..9d4fa53 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 @@
-8fddd0322d8762f8c2ad53af81119c8bf66ab02b
\ No newline at end of file
+e57ed8261c20e15f4e09c61b529f8365589a90f9
\ 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 502b210..6021838 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 @@
-9733d65af26b024f1773b31f8c8fa1dfd71ca505
\ No newline at end of file
+5e675089f680c8c12254bda1df044c1dd745aa89
\ 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 cba8d5c..b5176461 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 @@
-666b091033f45f609abb12f8fb193f05ea5ec520
\ No newline at end of file
+710bda4ac1399dc3d4c7af4d18743dbcabd3adf6
\ 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 fa39df2..acbcfe0 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 @@
-ae73376e001a0f5ed60206f508cbcf98d5117907
\ No newline at end of file
+0a2108df5d5c8f26b7ccd7880d8bceba6100b0c1
\ 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 1287fb9..0b1f1c8 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 @@
-39b5f954d4519524d7a74d967135539f5e7ca771
\ No newline at end of file
+eb91cc4ed861242d55705436176edf47c762199c
\ 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 938d29f..93698d4 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 @@
-ab60a43f8788572f058b489b1cea3a920467eb36
\ No newline at end of file
+c3441b269dbc781c54b379abacea6f01d057d162
\ 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 c6f991e..8e931ed 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 @@
-af479084aaef709cf5dd98f19163505fa8946bd4
\ No newline at end of file
+6501951aaf379fea4bbf957f1cd2dc64acbe443c
\ 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 b2c3358..3a1034e 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 @@
-cfb6b43d5ce37096bdf7c3fd8f63ea152cbcf0a9
\ No newline at end of file
+af2074724543e561b8307f41020bbb3e7729fcdc
\ 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 74aea5ac..37cd8ac4 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 @@
-495c0bd1749830ce69fb4604ce25f2e4c0874369
\ No newline at end of file
+74c59d188d011cdcac82b454e5f3a66f4fdbeb9c
\ 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 208f5e92..2691c41 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 @@
-1b1dfde6474eb0c4fa5e3660d76ab1d401e4e143
\ No newline at end of file
+828b58204cab699eebf1d5eeed2806600e2573e0
\ No newline at end of file
diff --git a/ios/showcase/infobars/BUILD.gn b/ios/showcase/infobars/BUILD.gn
index 62ad235..ac37987 100644
--- a/ios/showcase/infobars/BUILD.gn
+++ b/ios/showcase/infobars/BUILD.gn
@@ -16,9 +16,9 @@
   deps = [
     ":constants",
     "//base",
+    "//ios/chrome/browser/autofill/message",
     "//ios/chrome/browser/infobars:public",
     "//ios/chrome/browser/shared/ui/symbols",
-    "//ios/chrome/browser/ui/autofill:autofill_message",
     "//ios/chrome/browser/ui/infobars/banners",
     "//ios/chrome/browser/ui/infobars/coordinators",
     "//ios/chrome/browser/ui/infobars/modals",
diff --git a/ios/showcase/infobars/sc_infobar_modal_save_card_coordinator.mm b/ios/showcase/infobars/sc_infobar_modal_save_card_coordinator.mm
index 972e115..469b4b71 100644
--- a/ios/showcase/infobars/sc_infobar_modal_save_card_coordinator.mm
+++ b/ios/showcase/infobars/sc_infobar_modal_save_card_coordinator.mm
@@ -4,8 +4,8 @@
 
 #import "ios/showcase/infobars/sc_infobar_modal_save_card_coordinator.h"
 
+#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
-#import "ios/chrome/browser/ui/autofill/save_card_message_with_links.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_modal_delegate.h"
diff --git a/media/audio/audio_output_device_thread_callback.cc b/media/audio/audio_output_device_thread_callback.cc
index d80325d..98f54db 100644
--- a/media/audio/audio_output_device_thread_callback.cc
+++ b/media/audio/audio_output_device_thread_callback.cc
@@ -87,7 +87,6 @@
     }
   }
 
-  media::CheckGlitchInfoAndDelay(glitch_info, delay);
   // Update the audio-delay measurement, inform about the number of skipped
   // frames, and ask client to render audio.  Since |output_bus_| is wrapping
   // the shared memory the Render() call is writing directly into the shared
diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc
index 49219f80..9b6b2f61 100644
--- a/media/audio/audio_output_resampler.cc
+++ b/media/audio/audio_output_resampler.cc
@@ -25,7 +25,6 @@
 #include "media/audio/audio_output_dispatcher_impl.h"
 #include "media/audio/audio_output_proxy.h"
 #include "media/base/audio_converter.h"
-#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/limits.h"
 #include "media/base/sample_rates.h"
@@ -468,7 +467,6 @@
                                     AudioBus* dest) {
   TRACE_EVENT2("audio", "OnMoreDataConverter::OnMoreData", "input buffer size",
                input_buffer_size_, "output buffer size", output_buffer_size_);
-  CheckGlitchInfoAndDelay(glitch_info, delay);
   current_delay_ = delay;
   current_delay_timestamp_ = delay_timestamp;
   audio_converter_.ConvertWithInfo(0, glitch_info, dest);
diff --git a/media/audio/mac/audio_auhal_mac.cc b/media/audio/mac/audio_auhal_mac.cc
index afcbdac1..203e30b 100644
--- a/media/audio/mac/audio_auhal_mac.cc
+++ b/media/audio/mac/audio_auhal_mac.cc
@@ -20,7 +20,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "media/audio/mac/core_audio_util_mac.h"
-#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_pull_fifo.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/mac/channel_layout_util_mac.h"
@@ -367,9 +366,7 @@
   const base::TimeDelta delay = playout_time - now;
 
   // Supply the input data and render the output data.
-  auto glitch_info = glitch_info_accumulator_.GetAndReset();
-  CheckGlitchInfoAndDelay(glitch_info, delay);
-  source_->OnMoreData(delay, now, glitch_info, dest);
+  source_->OnMoreData(delay, now, glitch_info_accumulator_.GetAndReset(), dest);
   dest->Scale(volume_);
 }
 
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index a9c761f..3084487 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -752,10 +752,9 @@
       std::unique_ptr<AudioBus> audio_bus(
           AudioBus::WrapMemory(params_, audio_data));
       audio_bus_->set_is_bitstream_format(true);
-      auto glitch_info = glitch_info_accumulator.GetAndReset();
-      CheckGlitchInfoAndDelay(glitch_info, delay);
-      int frames_filled = source_->OnMoreData(delay, delay_timestamp,
-                                              glitch_info, audio_bus.get());
+      int frames_filled = source_->OnMoreData(
+          delay, delay_timestamp, glitch_info_accumulator.GetAndReset(),
+          audio_bus.get());
 
       // During pause/seek, keep the pipeline filled with zero'ed frames.
       if (!frames_filled) {
@@ -771,10 +770,9 @@
       return true;
     }
 #endif  // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
-    auto glitch_info = glitch_info_accumulator.GetAndReset();
-    CheckGlitchInfoAndDelay(glitch_info, delay);
-    int frames_filled = source_->OnMoreData(delay, delay_timestamp, glitch_info,
-                                            audio_bus_.get());
+    int frames_filled = source_->OnMoreData(
+        delay, delay_timestamp, glitch_info_accumulator.GetAndReset(),
+        audio_bus_.get());
     uint32_t num_filled_bytes = frames_filled * format_.Format.nBlockAlign;
     DCHECK_LE(num_filled_bytes, packet_size_bytes_);
     audio_bus_->Scale(volume_);
diff --git a/media/base/audio_glitch_info.cc b/media/base/audio_glitch_info.cc
index cf7b77a..8b2ef5c 100644
--- a/media/base/audio_glitch_info.cc
+++ b/media/base/audio_glitch_info.cc
@@ -40,15 +40,4 @@
   return temp;
 }
 
-void CheckGlitchInfoAndDelay(const AudioGlitchInfo& glitch_info,
-                             const base::TimeDelta delay) {
-  CHECK(glitch_info.duration < base::Seconds(1000));
-  CHECK(glitch_info.duration < base::Seconds(100));
-  CHECK(glitch_info.duration < base::Seconds(10));
-
-  CHECK(glitch_info.duration >= base::Seconds(-100));
-  CHECK(glitch_info.duration >= base::Seconds(-10));
-  CHECK(glitch_info.duration >= base::Seconds(0));
-}
-
 }  // namespace media
diff --git a/media/base/audio_glitch_info.h b/media/base/audio_glitch_info.h
index a11c417..2557c4c 100644
--- a/media/base/audio_glitch_info.h
+++ b/media/base/audio_glitch_info.h
@@ -47,10 +47,6 @@
   AudioGlitchInfo pending_info_;
 };
 
-// Used to investigate https://crbug.com/1449671
-MEDIA_EXPORT void CheckGlitchInfoAndDelay(const AudioGlitchInfo& glitch_info,
-                                          const base::TimeDelta delay);
-
 }  // namespace media
 
 #endif  // MEDIA_BASE_AUDIO_GLITCH_INFO_H_
diff --git a/media/renderers/video_frame_rgba_to_yuva_converter.cc b/media/renderers/video_frame_rgba_to_yuva_converter.cc
index ecc3717..a1ba24f 100644
--- a/media/renderers/video_frame_rgba_to_yuva_converter.cc
+++ b/media/renderers/video_frame_rgba_to_yuva_converter.cc
@@ -166,11 +166,26 @@
           dst_video_frame->mailbox_holder(0);
       ri->WaitSyncTokenCHROMIUM(dst_mailbox_holder.sync_token.GetConstData());
 
-      ri->CopySharedImage(
-          src_mailbox_holder.mailbox, dst_mailbox_holder.mailbox, GL_TEXTURE_2D,
-          0, 0, 0, 0, dst_video_frame->coded_size().width(),
-          dst_video_frame->coded_size().height(), /*unpack_flip_y=*/false,
-          /*unpack_premultiply_alpha=*/false);
+      // `unpack_flip_y` should be set if the surface origin of the source
+      // doesn't match that of the destination, which is created with
+      // kTopLeft_GrSurfaceOrigin.
+      // TODO(blundell): If this codepath is used with destinations that are
+      // created with other surface origins, will need to generalize this.
+      bool unpack_flip_y = (src_surface_origin != kTopLeft_GrSurfaceOrigin);
+
+      // Note: the destination video frame can have a coded size that is larger
+      // than that of the source video to account for alignment needs. In this
+      // case, CopySharedImage() will clear out the destination SharedImage and
+      // then copy only the size of the source video texture. This is different
+      // than the legacy codepath above, which stretches to fill the
+      // destination. Cropping seems clearly more correct, but on discovering
+      // this issue we did not want to risk making changes to the legacy
+      // codepath, which will go away once MultiplanarSharedImage fully rolls
+      // out.
+      ri->CopySharedImage(src_mailbox_holder.mailbox,
+                          dst_mailbox_holder.mailbox, GL_TEXTURE_2D, 0, 0, 0, 0,
+                          src_size.width(), src_size.height(), unpack_flip_y,
+                          /*unpack_premultiply_alpha=*/false);
     }
   } else {
     // Create an accelerated SkImage for the source.
diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc
index de6da17..2dca801 100644
--- a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc
@@ -21,6 +21,7 @@
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
+#include "media/base/media_switches.h"
 #include "media/base/video_frame.h"
 
 namespace media {
@@ -190,10 +191,6 @@
 
   gpu_memory_buffer_->SetColorSpace(color_space_);
 
-  // Bind SharedImages to each plane.
-  constexpr size_t kNumPlanes = 2;
-  constexpr gfx::BufferPlane kPlanes[kNumPlanes] = {gfx::BufferPlane::Y,
-                                                    gfx::BufferPlane::UV};
   constexpr uint32_t kSharedImageUsage =
 #if BUILDFLAG(IS_MAC)
       gpu::SHARED_IMAGE_USAGE_MACOS_VIDEO_TOOLBOX |
@@ -205,7 +202,22 @@
 #if BUILDFLAG(IS_MAC)
   // TODO(https://crbug.com/1311844): Use gpu::GetBufferTextureTarget() instead.
   texture_target = gpu::GetPlatformSpecificTextureTarget();
+
+  if (IsMultiPlaneFormatForHardwareVideoEnabled()) {
+    context->CreateSharedImage(
+        gpu_memory_buffer_.get(), viz::MultiPlaneFormat::kNV12, color_space_,
+        kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, kSharedImageUsage,
+        mailbox_holders_[0].mailbox, mailbox_holders_[0].sync_token);
+    mailbox_holders_[0].texture_target = texture_target;
+    return true;
+  }
 #endif
+
+  // Bind SharedImages to each plane.
+  constexpr size_t kNumPlanes = 2;
+  constexpr gfx::BufferPlane kPlanes[kNumPlanes] = {gfx::BufferPlane::Y,
+                                                    gfx::BufferPlane::UV};
+
   for (size_t plane = 0; plane < kNumPlanes; ++plane) {
     context->CreateSharedImage(
         gpu_memory_buffer_.get(), kPlanes[plane], color_space_,
@@ -239,6 +251,17 @@
   // format.
   video_frame->metadata().allow_overlay = true;
 
+#if BUILDFLAG(IS_MAC)
+  if (IsMultiPlaneFormatForHardwareVideoEnabled()) {
+    // Tag this frame as having used a single SharedImage for multiplanar
+    // formats (by default it sets this field to `kLegacy`, which causes the
+    // rest of the system to assume that this frame has been created with one
+    // SharedImage per plane for multiplanar formats).
+    video_frame->set_shared_image_format_type(
+        SharedImageFormatType::kSharedImageFormat);
+  }
+#endif
+
   // Only native (non shared memory) GMBs require waiting on GPU fences.
   const bool has_native_gmb =
       video_frame->HasGpuMemoryBuffer() &&
diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool.h b/media/video/renderable_gpu_memory_buffer_video_frame_pool.h
index babfabf..1a0196e1 100644
--- a/media/video/renderable_gpu_memory_buffer_video_frame_pool.h
+++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool.h
@@ -27,6 +27,10 @@
 struct SyncToken;
 }  // namespace gpu
 
+namespace viz {
+class SharedImageFormat;
+}
+
 namespace media {
 
 class VideoFrame;
@@ -57,6 +61,18 @@
                                    gpu::Mailbox& mailbox,
                                    gpu::SyncToken& sync_token) = 0;
 
+    // Create a SharedImage representation with format `si_format` of a
+    // GpuMemoryBuffer allocated by this interface. Populate `mailbox` and
+    // `sync_token`.
+    virtual void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
+                                   const viz::SharedImageFormat& si_format,
+                                   const gfx::ColorSpace& color_space,
+                                   GrSurfaceOrigin surface_origin,
+                                   SkAlphaType alpha_type,
+                                   uint32_t usage,
+                                   gpu::Mailbox& mailbox,
+                                   gpu::SyncToken& sync_token) = 0;
+
     // Destroy a SharedImage created by this interface.
     virtual void DestroySharedImage(const gpu::SyncToken& sync_token,
                                     const gpu::Mailbox& mailbox) = 0;
diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc b/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc
index c3acd03..7fc13922 100644
--- a/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc
+++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc
@@ -8,7 +8,11 @@
 #include "base/memory/weak_ptr.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "components/viz/common/resources/shared_image_format.h"
+#include "gpu/config/gpu_finch_features.h"
+#include "media/base/media_switches.h"
 #include "media/base/video_frame.h"
 #include "media/video/fake_gpu_memory_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -33,6 +37,19 @@
     return std::make_unique<FakeGpuMemoryBuffer>(size, format);
   }
   void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
+                         const viz::SharedImageFormat& si_format,
+                         const gfx::ColorSpace& color_space,
+                         GrSurfaceOrigin surface_origin,
+                         SkAlphaType alpha_type,
+                         uint32_t usage,
+                         gpu::Mailbox& mailbox,
+                         gpu::SyncToken& sync_token) override {
+    DoCreateSharedImage(si_format, gpu_memory_buffer->GetSize(), color_space,
+                        surface_origin, alpha_type, usage,
+                        gpu_memory_buffer->CloneHandle());
+    mailbox = gpu::Mailbox::GenerateForSharedImage();
+  }
+  void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
                          gfx::BufferPlane plane,
                          const gfx::ColorSpace& color_space,
                          GrSurfaceOrigin surface_origin,
@@ -47,6 +64,14 @@
 
   MOCK_METHOD2(DoCreateGpuMemoryBuffer,
                void(const gfx::Size& size, gfx::BufferFormat format));
+  MOCK_METHOD7(DoCreateSharedImage,
+               void(viz::SharedImageFormat format,
+                    const gfx::Size& size,
+                    const gfx::ColorSpace& color_space,
+                    GrSurfaceOrigin surface_origin,
+                    SkAlphaType alpha_type,
+                    uint32_t usage,
+                    gfx::GpuMemoryBufferHandle buffer_handle));
   MOCK_METHOD6(DoCreateSharedImage,
                void(gfx::GpuMemoryBuffer* gpu_memory_buffer,
                     gfx::BufferPlane plane,
@@ -64,7 +89,50 @@
   base::WeakPtrFactory<FakeContext> weak_factory_;
 };
 
-TEST(RenderableGpuMemoryBufferVideoFramePool, SimpleLifetimes) {
+class RenderableGpuMemoryBufferVideoFramePoolTest
+    : public testing::TestWithParam<bool> {
+ public:
+  RenderableGpuMemoryBufferVideoFramePoolTest() {
+    if (GetParam()) {
+      scoped_feature_list_.InitWithFeatures(
+          {features::kPassthroughYuvRgbConversion,
+           kUseMultiPlaneFormatForHardwareVideo},
+          {});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          {}, {features::kPassthroughYuvRgbConversion,
+               kUseMultiPlaneFormatForHardwareVideo});
+    }
+  }
+
+ protected:
+  void VerifySharedImageCreation(FakeContext* context) {
+#if BUILDFLAG(IS_MAC)
+    if (GetParam()) {
+      EXPECT_CALL(*context, DoCreateSharedImage(viz::MultiPlaneFormat::kNV12, _,
+                                                _, _, _, _, _));
+      return;
+    }
+#endif
+    EXPECT_CALL(*context,
+                DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
+    EXPECT_CALL(*context,
+                DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  }
+
+  int NumSharedImagesPerFrame() {
+#if BUILDFLAG(IS_MAC)
+    if (GetParam()) {
+      return 1;
+    }
+#endif
+    return 2;
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_P(RenderableGpuMemoryBufferVideoFramePoolTest, SimpleLifetimes) {
   base::test::SingleThreadTaskEnvironment task_environment;
   const gfx::BufferFormat format = gfx::BufferFormat::YUV_420_BIPLANAR;
   const gfx::Size size0(128, 256);
@@ -81,10 +149,7 @@
 
   // Create a new frame.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   auto video_frame0 = pool->MaybeCreateVideoFrame(size0, color_space0);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
@@ -92,22 +157,17 @@
   // Expect the frame to be reused.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format)).Times(0);
   EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _)).Times(0);
+  EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _, _)).Times(0);
   auto video_frame1 = pool->MaybeCreateVideoFrame(size0, color_space0);
 
   // Expect a new frame to be created.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   auto video_frame2 = pool->MaybeCreateVideoFrame(size0, color_space0);
 
   // Expect a new frame to be created.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   auto video_frame3 = pool->MaybeCreateVideoFrame(size0, color_space0);
 
   // Freeing two frames will not result in any frames being destroyed, because
@@ -117,20 +177,22 @@
   task_environment.RunUntilIdle();
 
   // Freeing the third frame will result in one of the frames being destroyed.
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(2);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(NumSharedImagesPerFrame());
   video_frame3 = nullptr;
   task_environment.RunUntilIdle();
 
   // Destroying the pool will result in the remaining two frames being
   // destroyed.
   EXPECT_TRUE(!!context);
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(4);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(NumSharedImagesPerFrame() * 2);
   pool.reset();
   task_environment.RunUntilIdle();
   EXPECT_FALSE(!!context);
 }
 
-TEST(RenderableGpuMemoryBufferVideoFramePool, FrameFreedAfterPool) {
+TEST_P(RenderableGpuMemoryBufferVideoFramePoolTest, FrameFreedAfterPool) {
   base::test::SingleThreadTaskEnvironment task_environment;
   const gfx::BufferFormat format = gfx::BufferFormat::YUV_420_BIPLANAR;
   const gfx::Size size0(128, 256);
@@ -147,10 +209,7 @@
 
   // Create a new frame.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   auto video_frame0 = pool->MaybeCreateVideoFrame(size0, color_space0);
   task_environment.RunUntilIdle();
 
@@ -165,12 +224,13 @@
   video_frame0 = nullptr;
 
   // The shared images will be destroyed once the posted task is run.
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(2);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(NumSharedImagesPerFrame());
   task_environment.RunUntilIdle();
   EXPECT_FALSE(!!context);
 }
 
-TEST(RenderableGpuMemoryBufferVideoFramePool, CrossThread) {
+TEST_P(RenderableGpuMemoryBufferVideoFramePoolTest, CrossThread) {
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   const gfx::Size size0(128, 256);
@@ -195,8 +255,8 @@
   task_environment.RunUntilIdle();
 }
 
-TEST(RenderableGpuMemoryBufferVideoFramePool,
-     VideoFramesDestroyedConcurrently) {
+TEST_P(RenderableGpuMemoryBufferVideoFramePoolTest,
+       VideoFramesDestroyedConcurrently) {
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   const gfx::BufferFormat format = gfx::BufferFormat::YUV_420_BIPLANAR;
@@ -217,16 +277,14 @@
   static constexpr int kNumFrames = 3;
   for (int i = 0; i < kNumFrames; i++) {
     EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format));
-    EXPECT_CALL(*context,
-                DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-    EXPECT_CALL(*context,
-                DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+    VerifySharedImageCreation(context.get());
     frames.emplace_back(pool->MaybeCreateVideoFrame(size0, color_space0));
   }
   task_environment.RunUntilIdle();
 
   // Expect all frames to be destroyed eventually.
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(kNumFrames * 2);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(kNumFrames * NumSharedImagesPerFrame());
 
   // Destroy frames on separate threads. TSAN will tell us if there's a problem.
   for (int i = 0; i < kNumFrames; i++) {
@@ -239,7 +297,7 @@
   EXPECT_FALSE(!!context);
 }
 
-TEST(RenderableGpuMemoryBufferVideoFramePool, ConcurrentCreateDestroy) {
+TEST_P(RenderableGpuMemoryBufferVideoFramePoolTest, ConcurrentCreateDestroy) {
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   const gfx::Size size0(128, 256);
@@ -267,7 +325,7 @@
   task_environment.RunUntilIdle();
 }
 
-TEST(RenderableGpuMemoryBufferVideoFramePool, RespectSizeAndColorSpace) {
+TEST_P(RenderableGpuMemoryBufferVideoFramePoolTest, RespectSizeAndColorSpace) {
   base::test::SingleThreadTaskEnvironment task_environment;
   const gfx::BufferFormat format = gfx::BufferFormat::YUV_420_BIPLANAR;
   const gfx::Size size0(128, 256);
@@ -286,10 +344,7 @@
 
   // Create a new frame.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size0, format)).Times(1);
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   auto video_frame0 = pool->MaybeCreateVideoFrame(size0, color_space0);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
@@ -297,18 +352,17 @@
   // Expect the frame to be reused.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(_, _)).Times(0);
   EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _)).Times(0);
+  EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _, _)).Times(0);
   video_frame0 = pool->MaybeCreateVideoFrame(size0, color_space0);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
 
   // Change the size, expect a new frame to be created (and the previous frame
   // to be destroyed).
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(2);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(NumSharedImagesPerFrame());
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size1, format));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   video_frame0 = pool->MaybeCreateVideoFrame(size1, color_space0);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
@@ -316,18 +370,17 @@
   // Expect that frame to be reused.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(_, _)).Times(0);
   EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _)).Times(0);
+  EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _, _)).Times(0);
   video_frame0 = pool->MaybeCreateVideoFrame(size1, color_space0);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
 
   // Change the color space, expect a new frame to be created (and the previous
   // frame to be destroyed).
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(2);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(NumSharedImagesPerFrame());
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(size1, format));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::Y, _, _, _, _));
-  EXPECT_CALL(*context,
-              DoCreateSharedImage(_, gfx::BufferPlane::UV, _, _, _, _));
+  VerifySharedImageCreation(context.get());
   video_frame0 = pool->MaybeCreateVideoFrame(size1, color_space1);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
@@ -335,16 +388,22 @@
   // Expect that frame to be reused.
   EXPECT_CALL(*context, DoCreateGpuMemoryBuffer(_, _)).Times(0);
   EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _)).Times(0);
+  EXPECT_CALL(*context, DoCreateSharedImage(_, _, _, _, _, _, _)).Times(0);
   video_frame0 = pool->MaybeCreateVideoFrame(size1, color_space1);
   video_frame0 = nullptr;
   task_environment.RunUntilIdle();
 
-  EXPECT_CALL(*context, DestroySharedImage(_, _)).Times(2);
+  EXPECT_CALL(*context, DestroySharedImage(_, _))
+      .Times(NumSharedImagesPerFrame());
   pool.reset();
   task_environment.RunUntilIdle();
   EXPECT_FALSE(!!context);
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         RenderableGpuMemoryBufferVideoFramePoolTest,
+                         testing::Bool());
+
 }  // namespace
 
 }  // namespace media
diff --git a/services/audio/mixing_graph_impl.cc b/services/audio/mixing_graph_impl.cc
index 007b62ee..5d6e3ec 100644
--- a/services/audio/mixing_graph_impl.cc
+++ b/services/audio/mixing_graph_impl.cc
@@ -224,7 +224,6 @@
   TRACE_EVENT_BEGIN2(TRACE_DISABLED_BY_DEFAULT("audio"),
                      "MixingGraphImpl::OnMoreData", "delay", delay,
                      "delay_timestamp", delay_timestamp);
-  CheckGlitchInfoAndDelay(glitch_info, delay);
 
   uint32_t frames_delayed = media::AudioTimestampHelper::TimeToFrames(
       delay, output_params_.sample_rate());
diff --git a/services/audio/sync_reader.cc b/services/audio/sync_reader.cc
index e3d8047..424c802 100644
--- a/services/audio/sync_reader.cc
+++ b/services/audio/sync_reader.cc
@@ -20,7 +20,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "media/audio/audio_device_thread.h"
-#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/media_switches.h"
 #include "services/audio/output_glitch_counter.h"
@@ -163,9 +162,6 @@
     // returned after the browser stops the output device in response to a
     // renderer side request.
     control_signal = std::numeric_limits<uint32_t>::max();
-  } else {
-    media::CheckGlitchInfoAndDelay(glitch_info, delay);
-    media::CheckGlitchInfoAndDelay(pending_glitch_info_, delay);
   }
 
   size_t sent_bytes = socket_.Send(&control_signal, sizeof(control_signal));
diff --git a/services/network/attribution/attribution_request_helper.cc b/services/network/attribution/attribution_request_helper.cc
index 2f8cb6b..885484b 100644
--- a/services/network/attribution/attribution_request_helper.cc
+++ b/services/network/attribution/attribution_request_helper.cc
@@ -294,9 +294,12 @@
     return;
   }
 
+  uint64_t grease_bits = base::RandUint64();
+
   std::string eligible_header = SerializeAttributionReportingEligibleHeader(
       request.attribution_reporting_eligibility,
-      AttributionReportingEligibleGreaseOptions::FromBits(base::RandUint64()));
+      AttributionReportingHeaderGreaseOptions::FromBits(grease_bits & 0xff));
+  grease_bits >>= 8;
 
   url_request.SetExtraRequestHeaderByName("Attribution-Reporting-Eligible",
                                           std::move(eligible_header),
@@ -312,8 +315,12 @@
           features::kAttributionReportingCrossAppWeb)) {
     url_request.SetExtraRequestHeaderByName(
         "Attribution-Reporting-Support",
-        GetAttributionSupportHeader(request.attribution_reporting_support),
+        GetAttributionSupportHeader(
+            request.attribution_reporting_support,
+            AttributionReportingHeaderGreaseOptions::FromBits(grease_bits &
+                                                              0xff)),
         /*overwrite=*/true);
+    grease_bits >>= 8;
   }
 }
 
diff --git a/services/network/attribution/request_headers_internal.cc b/services/network/attribution/request_headers_internal.cc
index 57bfc56..cae19d1 100644
--- a/services/network/attribution/request_headers_internal.cc
+++ b/services/network/attribution/request_headers_internal.cc
@@ -16,7 +16,6 @@
 #include "base/strings/strcat.h"
 #include "base/types/cxx23_to_underlying.h"
 #include "net/http/structured_headers.h"
-#include "services/network/public/cpp/attribution_utils.h"
 #include "services/network/public/mojom/attribution.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -25,7 +24,7 @@
 namespace {
 
 using GreaseContext =
-    ::network::AttributionReportingEligibleGreaseOptions::GreaseContext;
+    ::network::AttributionReportingHeaderGreaseOptions::GreaseContext;
 
 using ::network::mojom::AttributionReportingEligibility;
 
@@ -38,12 +37,77 @@
                         net::structured_headers::Parameters()));
 }
 
+void ApplyGreaseContext(
+    std::vector<net::structured_headers::DictionaryMember>& dict,
+    GreaseContext context,
+    bool use_front,
+    const char* grease) {
+  if (!grease) {
+    return;
+  }
+
+  switch (context) {
+    case GreaseContext::kNone:
+      break;
+    case GreaseContext::kKey:
+      AddTrueValuedDictMember(dict, base::StrCat({"not-", grease}));
+      break;
+    case GreaseContext::kValue:
+      if (!dict.empty()) {
+        auto& [_, parameterized_member] =
+            use_front ? dict.front() : dict.back();
+        parameterized_member.member.front().item =
+            net::structured_headers::Item(
+                grease, net::structured_headers::Item::kTokenType);
+      }
+      break;
+    case GreaseContext::kParamName:
+      if (!dict.empty()) {
+        auto& [_, parameterized_member] =
+            use_front ? dict.front() : dict.back();
+        parameterized_member.params.emplace_back(
+            grease, net::structured_headers::Item(true));
+      }
+      break;
+  }
+}
+
+void ApplyGrease(std::vector<net::structured_headers::DictionaryMember>& dict,
+                 const AttributionReportingHeaderGreaseOptions& options,
+                 const char* grease1,
+                 const char* grease2) {
+  // We "grease" the header with meaningless components to help ensure that
+  // recipients are using a structured header parser, not naive string
+  // operations on the serialized header, and to ensure that values and
+  // parameters are ignored. We carefully choose the greases themselves to
+  // minimize excessive bandwidth: each substring corresponding to a real key
+  // will appear at most once in the serialized header.
+  //
+  // https://wicg.github.io/attribution-reporting-api/#set-attribution-reporting-headers
+
+  // Allowing these to be swapped gives them equal chance of being used, since
+  // otherwise a key is more likely to exist for the second grease than the
+  // first.
+  if (options.swap_greases) {
+    std::swap(grease1, grease2);
+  }
+
+  ApplyGreaseContext(dict, options.context1, options.use_front1, grease1);
+  ApplyGreaseContext(dict, options.context2, options.use_front2, grease2);
+
+  if (dict.size() > 1 && options.reverse) {
+    // Dictionaries retain order during serialization, so reordering helps
+    // ensure that recipients do not depend on it.
+    base::ranges::reverse(dict);
+  }
+}
+
 }  // namespace
 
 // static
-AttributionReportingEligibleGreaseOptions
-AttributionReportingEligibleGreaseOptions::FromBits(uint64_t bits) {
-  AttributionReportingEligibleGreaseOptions options;
+AttributionReportingHeaderGreaseOptions
+AttributionReportingHeaderGreaseOptions::FromBits(uint8_t bits) {
+  AttributionReportingHeaderGreaseOptions options;
 
   options.reverse = bits & 0b1;
   bits >>= 1;
@@ -73,7 +137,7 @@
 
 std::string SerializeAttributionReportingEligibleHeader(
     const AttributionReportingEligibility eligibility,
-    const AttributionReportingEligibleGreaseOptions& options) {
+    const AttributionReportingHeaderGreaseOptions& options) {
   const char* const kEventSource = "event-source";
   const char* const kNavigationSource = "navigation-source";
   const char* const kTrigger = "trigger";
@@ -112,63 +176,7 @@
       break;
   }
 
-  // We "grease" the header with meaningless components to help ensure that
-  // recipients are using a structured header parser, not naive string
-  // operations on the serialized header, and to ensure that values and
-  // parameters are ignored. We carefully choose the greases themselves to
-  // minimize excessive bandwidth: each substring "event-source",
-  // "navigation-source", and "trigger" will appear at most once in the
-  // serialized header.
-  //
-  // https://wicg.github.io/attribution-reporting-api/#set-attribution-reporting-headers
-
-  // Allowing these to be swapped gives them equal chance of being used, since
-  // otherwise a key is more likely to exist for the second grease than the
-  // first.
-  if (options.swap_greases) {
-    std::swap(grease1, grease2);
-  }
-
-  const auto apply_grease = [&](GreaseContext context, bool use_front,
-                                const char* grease) {
-    if (!grease) {
-      return;
-    }
-
-    switch (context) {
-      case GreaseContext::kNone:
-        break;
-      case GreaseContext::kKey:
-        AddTrueValuedDictMember(eligibilities, base::StrCat({"not-", grease}));
-        break;
-      case GreaseContext::kValue:
-        if (!eligibilities.empty()) {
-          auto& [_, parameterized_member] =
-              use_front ? eligibilities.front() : eligibilities.back();
-          parameterized_member.member.front().item =
-              net::structured_headers::Item(
-                  grease, net::structured_headers::Item::kTokenType);
-        }
-        break;
-      case GreaseContext::kParamName:
-        if (!eligibilities.empty()) {
-          auto& [_, parameterized_member] =
-              use_front ? eligibilities.front() : eligibilities.back();
-          parameterized_member.params.emplace_back(
-              grease, net::structured_headers::Item(true));
-        }
-        break;
-    }
-  };
-
-  apply_grease(options.context1, options.use_front1, grease1);
-  apply_grease(options.context2, options.use_front2, grease2);
-
-  if (eligibilities.size() > 1 && options.reverse) {
-    // Dictionaries retain order during serialization, so reordering helps
-    // ensure that recipients do not depend on it.
-    base::ranges::reverse(eligibilities);
-  }
+  ApplyGrease(eligibilities, options, grease1, grease2);
 
   absl::optional<std::string> eligible_header =
       net::structured_headers::SerializeDictionary(
@@ -179,15 +187,36 @@
 }
 
 std::string GetAttributionSupportHeader(
-    mojom::AttributionSupport attribution_support) {
+    mojom::AttributionSupport attribution_support,
+    const AttributionReportingHeaderGreaseOptions& options) {
   std::vector<net::structured_headers::DictionaryMember> registrars;
 
-  if (HasAttributionOsSupport(attribution_support)) {
-    AddTrueValuedDictMember(registrars, "os");
+  const char* grease1;
+  const char* grease2;
+  switch (attribution_support) {
+    case mojom::AttributionSupport::kWeb:
+      AddTrueValuedDictMember(registrars, "web");
+      grease1 = "os";
+      grease2 = nullptr;
+      break;
+    case mojom::AttributionSupport::kOs:
+      AddTrueValuedDictMember(registrars, "os");
+      grease1 = "web";
+      grease2 = nullptr;
+      break;
+    case mojom::AttributionSupport::kWebAndOs:
+      AddTrueValuedDictMember(registrars, "os");
+      AddTrueValuedDictMember(registrars, "web");
+      grease1 = nullptr;
+      grease2 = nullptr;
+      break;
+    case mojom::AttributionSupport::kNone:
+      grease1 = "os";
+      grease2 = "web";
+      break;
   }
-  if (HasAttributionWebSupport(attribution_support)) {
-    AddTrueValuedDictMember(registrars, "web");
-  }
+
+  ApplyGrease(registrars, options, grease1, grease2);
 
   absl::optional<std::string> support_header =
       net::structured_headers::SerializeDictionary(
diff --git a/services/network/attribution/request_headers_internal.h b/services/network/attribution/request_headers_internal.h
index c368feb..de8f803 100644
--- a/services/network/attribution/request_headers_internal.h
+++ b/services/network/attribution/request_headers_internal.h
@@ -14,9 +14,9 @@
 namespace network {
 
 // Options controlling greasing during serialization of the
-// Attribution-Reporting-Eligible header, which contains a structured
-// dictionary.
-struct AttributionReportingEligibleGreaseOptions {
+// Attribution-Reporting-Eligible and Attribution-Reportnig-Support headers,
+// which contain structured dictionaries.
+struct AttributionReportingHeaderGreaseOptions {
   // Where to apply a grease.
   enum class GreaseContext : uint8_t {
     // The grease is not applied.
@@ -29,7 +29,9 @@
     kParamName = 3,
   };
 
-  static AttributionReportingEligibleGreaseOptions FromBits(uint64_t bits);
+  // Note: This function needs exactly 8 bits. If the size of this structure changes,
+  // `uint8_t` should be changed to a larger type.
+  static AttributionReportingHeaderGreaseOptions FromBits(uint8_t bits);
 
   // Whether to reverse the list of dictionary keys.
   bool reverse = false;
@@ -49,11 +51,13 @@
 // Must not be called with `mojom::AttributionReportingEligibility::kUnset`.
 std::string SerializeAttributionReportingEligibleHeader(
     mojom::AttributionReportingEligibility,
-    const AttributionReportingEligibleGreaseOptions&);
+    const AttributionReportingHeaderGreaseOptions&);
 
 // Returns the value to be set for the `Attribution-Reporting-Support` request
 // header.
-std::string GetAttributionSupportHeader(mojom::AttributionSupport);
+std::string GetAttributionSupportHeader(
+    mojom::AttributionSupport,
+    const AttributionReportingHeaderGreaseOptions&);
 
 }  // namespace network
 
diff --git a/services/network/attribution/request_headers_internal_unittest.cc b/services/network/attribution/request_headers_internal_unittest.cc
index 6cbda8c008..0321280 100644
--- a/services/network/attribution/request_headers_internal_unittest.cc
+++ b/services/network/attribution/request_headers_internal_unittest.cc
@@ -19,12 +19,14 @@
 
 namespace {
 using GreaseContext =
-    ::network::AttributionReportingEligibleGreaseOptions::GreaseContext;
+    ::network::AttributionReportingHeaderGreaseOptions::GreaseContext;
+
+using ::network::mojom::AttributionSupport;
 }  // namespace
 
-bool operator==(const AttributionReportingEligibleGreaseOptions& a,
-                const AttributionReportingEligibleGreaseOptions& b) {
-  const auto tie = [](const AttributionReportingEligibleGreaseOptions& o) {
+bool operator==(const AttributionReportingHeaderGreaseOptions& a,
+                const AttributionReportingHeaderGreaseOptions& b) {
+  const auto tie = [](const AttributionReportingHeaderGreaseOptions& o) {
     return std::make_tuple(o.reverse, o.swap_greases, o.context1, o.context2,
                            o.use_front1, o.use_front2);
   };
@@ -45,7 +47,7 @@
 }
 
 std::ostream& operator<<(std::ostream& out,
-                         const AttributionReportingEligibleGreaseOptions& o) {
+                         const AttributionReportingHeaderGreaseOptions& o) {
   return out << o.reverse << ", " << o.swap_greases << ", " << o.context1
              << ", " << o.context2 << ", " << o.use_front1 << ", "
              << o.use_front2;
@@ -57,8 +59,8 @@
 
 TEST(AttributionRequestHeadersTest, GreaseOptionsFromBits) {
   const struct {
-    uint64_t bits;
-    AttributionReportingEligibleGreaseOptions expected;
+    uint8_t bits;
+    AttributionReportingHeaderGreaseOptions expected;
   } kTestCases[] = {
       {
           0b00000000,
@@ -109,7 +111,7 @@
   for (const auto& test_case : kTestCases) {
     EXPECT_EQ(
         test_case.expected,
-        AttributionReportingEligibleGreaseOptions::FromBits(test_case.bits));
+        AttributionReportingHeaderGreaseOptions::FromBits(test_case.bits));
   }
 }
 
@@ -127,17 +129,17 @@
   };
 
   for (const auto& test_case : kTestCases) {
-    EXPECT_EQ(test_case.expected,
-              SerializeAttributionReportingEligibleHeader(
-                  test_case.eligibility,
-                  AttributionReportingEligibleGreaseOptions()));
+    EXPECT_EQ(
+        test_case.expected,
+        SerializeAttributionReportingEligibleHeader(
+            test_case.eligibility, AttributionReportingHeaderGreaseOptions()));
   }
 }
 
 TEST(AttributionRequestHeadersTest, Greases) {
   const struct {
     AttributionReportingEligibility eligibility;
-    AttributionReportingEligibleGreaseOptions options;
+    AttributionReportingHeaderGreaseOptions options;
     const char* expected;
   } kTestCases[] = {
       // reverse with vectors of varying lengths
@@ -307,7 +309,8 @@
     SCOPED_TRACE(test_case.attribution_support);
 
     std::string actual =
-        GetAttributionSupportHeader(test_case.attribution_support);
+        GetAttributionSupportHeader(test_case.attribution_support,
+                                    AttributionReportingHeaderGreaseOptions());
 
     auto dict = net::structured_headers::ParseDictionary(actual);
     EXPECT_TRUE(dict.has_value());
@@ -322,5 +325,86 @@
   }
 }
 
+TEST(AttributionRequestHeadersTest, Greases_Support) {
+  const struct {
+    AttributionSupport support;
+    AttributionReportingHeaderGreaseOptions options;
+    const char* expected;
+  } kTestCases[] = {
+      // reverse with vectors of varying lengths
+      {
+          AttributionSupport::kNone,
+          {.reverse = true},
+          "",
+      },
+      {
+          AttributionSupport::kWeb,
+          {.reverse = true},
+          "web",
+      },
+      {
+          AttributionSupport::kWebAndOs,
+          {.reverse = true},
+          "web, os",
+      },
+      // grease 1
+      {
+          AttributionSupport::kNone,
+          {.context1 = GreaseContext::kKey},
+          "not-os",
+      },
+      {
+          AttributionSupport::kWeb,
+          {.context1 = GreaseContext::kKey},
+          "web, not-os",
+      },
+      {
+          AttributionSupport::kWeb,
+          {.context1 = GreaseContext::kValue},
+          "web=os",
+      },
+      {
+          AttributionSupport::kWeb,
+          {.context1 = GreaseContext::kParamName},
+          "web;os",
+      },
+      {
+          AttributionSupport::kOs,
+          {.context1 = GreaseContext::kKey},
+          "os, not-web",
+      },
+      {
+          AttributionSupport::kOs,
+          {.context1 = GreaseContext::kValue},
+          "os=web",
+      },
+      {
+          AttributionSupport::kOs,
+          {.context1 = GreaseContext::kParamName},
+          "os;web",
+      },
+      {
+          AttributionSupport::kWebAndOs,
+          {.context1 = GreaseContext::kKey},
+          "os, web",
+      },
+      {
+          AttributionSupport::kWebAndOs,
+          {.context1 = GreaseContext::kValue},
+          "os, web",
+      },
+      {
+          AttributionSupport::kWebAndOs,
+          {.context1 = GreaseContext::kParamName},
+          "os, web",
+      },
+  };
+
+  for (const auto& test_case : kTestCases) {
+    EXPECT_EQ(test_case.expected, GetAttributionSupportHeader(
+                                      test_case.support, test_case.options));
+  }
+}
+
 }  // namespace
 }  // namespace network
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index a4e1809..bff25ab 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -83,7 +83,6 @@
 // cookies.
 const uint32 kWebSocketOptionBlockThirdPartyCookies = 2;
 
-// Indicates that execution is blocking on the completion of the request.
 // The browser can provide a CustomProxyConfig to a CustomProxyConfigClient
 // running in the network service. The network service will use the given proxy
 // configuration if a request matches the proxy rules and all the other
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 955b200..d46da4d2 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -10742,8 +10742,7 @@
           "--use-persistent-shell",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android31.textpb",
-          "--gtest_filter=-com.android.cts.webkit.WebViewDeviceSideStartupTest.testStrictModeNotViolatedOnStartup"
+          "--avd-config=../../tools/android/avd/proto/generic_android31.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -10817,8 +10816,7 @@
           "--use-persistent-shell",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android31.textpb",
-          "--gtest_filter=-com.android.cts.webkit.WebViewDeviceSideStartupTest.testStrictModeNotViolatedOnStartup"
+          "--avd-config=../../tools/android/avd/proto/generic_android31.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -30737,7 +30735,7 @@
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android24.textpb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_cts_tests.filter;../../testing/buildbot/filters/android.webview_cts_tests_coverage.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_cts_tests.filter"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -33135,8 +33133,7 @@
         "args": [
           "--use-persistent-shell",
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.webview_cts_tests_coverage.filter"
+          "--recover-devices"
         ],
         "isolate_profile_data": true,
         "merge": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 89828317..a7d31c9 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -7620,7 +7620,7 @@
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android24.textpb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_cts_tests.filter;../../testing/buildbot/filters/android.webview_cts_tests_coverage.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_cts_tests.filter"
         ],
         "isolate_profile_data": true,
         "merge": {
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 84a5c50..401e4953 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -378,10 +378,8 @@
 source_set("webview_cts_tests_filters") {
   testonly = true
 
-  data = [
-    "//testing/buildbot/filters/android.emulator.webview_cts_tests.filter",
-    "//testing/buildbot/filters/android.webview_cts_tests_coverage.filter",
-  ]
+  data =
+      [ "//testing/buildbot/filters/android.emulator.webview_cts_tests.filter" ]
 }
 
 source_set("webview_instrumentation_test_apk_filters") {
diff --git a/testing/buildbot/filters/android.webview_cts_tests_coverage.filter b/testing/buildbot/filters/android.webview_cts_tests_coverage.filter
deleted file mode 100644
index 30be875..0000000
--- a/testing/buildbot/filters/android.webview_cts_tests_coverage.filter
+++ /dev/null
@@ -1,2 +0,0 @@
-# crbug.com/1401476 Test doesn't work with code coverage.
--android.webkit.cts.WebViewStartupTest.testStrictModeNotViolatedOnStartup
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 435751b..255f1fb 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -4712,9 +4712,6 @@
   'webview_64_cts_tests': {
     'modifications': {
       'android-pie-arm64-rel': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/android.webview_cts_tests_coverage.filter',
-        ],
         # TODO(crbug.com/1111436): Move this back to walleye if/when additional
         # capacity has been deployed.
         'swarming': {
@@ -4733,7 +4730,6 @@
       'android-nougat-x86-rel': {
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_cts_tests.filter',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/android.webview_cts_tests_coverage.filter',
         ],
       },
       'android-pie-arm64-rel': {
@@ -4751,7 +4747,6 @@
       'android-x86-code-coverage': {
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_cts_tests.filter',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/android.webview_cts_tests_coverage.filter',
         ],
       },
     },
@@ -4851,26 +4846,6 @@
       },
     },
   },
-  'webview_trichrome_64_cts_tests full_mode': {
-    'modifications': {
-      'android-12-x64-rel': {
-        'args': [
-          # crbug.com/1401476: Test doesn't work with code coverage.
-          '--gtest_filter=-com.android.cts.webkit.WebViewDeviceSideStartupTest.testStrictModeNotViolatedOnStartup',
-        ],
-      }
-    }
-  },
-  'webview_trichrome_64_cts_tests instant_mode': {
-    'modifications': {
-      'android-12-x64-rel': {
-        'args': [
-          # crbug.com/1401476: Test doesn't work with code coverage.
-          '--gtest_filter=-com.android.cts.webkit.WebViewDeviceSideStartupTest.testStrictModeNotViolatedOnStartup',
-        ],
-      }
-    }
-  },
   'webview_ui_test_app_test_apk': {
     'remove_from': [
       'android-11-x86-rel', # crbug.com/1165280
diff --git a/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc b/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc
index 75880d5..9a4f4f7 100644
--- a/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc
+++ b/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc
@@ -256,6 +256,12 @@
       }
     }
 
+    if (gesture_data->tap_down_data &&
+        type == blink::WebInputEvent::Type::kGestureTapDown) {
+      gesture_event->data.tap_down.tap_down_count =
+          gesture_data->tap_down_data->tap_down_count;
+    }
+
     if (gesture_data->fling_data) {
       switch (type) {
         default:
@@ -447,6 +453,8 @@
       gesture_data->contact_size =
           gfx::Size(gesture_event->data.tap_down.width,
                     gesture_event->data.tap_down.height);
+      gesture_data->tap_down_data = blink::mojom::TapDownData::New(
+          gesture_event->data.tap_down.tap_down_count);
       break;
     case blink::WebInputEvent::Type::kGestureShowPress:
       gesture_data->contact_size =
diff --git a/third_party/blink/common/input/web_gesture_event.cc b/third_party/blink/common/input/web_gesture_event.cc
index 9584718e..3ff23c7e 100644
--- a/third_party/blink/common/input/web_gesture_event.cc
+++ b/third_party/blink/common/input/web_gesture_event.cc
@@ -229,6 +229,11 @@
   return data.tap.tap_count;
 }
 
+int WebGestureEvent::TapDownCount() const {
+  DCHECK_EQ(type_, WebInputEvent::Type::kGestureTapDown);
+  return data.tap_down.tap_down_count;
+}
+
 void WebGestureEvent::ApplyTouchAdjustment(
     const gfx::PointF& root_frame_coords) {
   // Update the window-relative position of the event so that the node that
diff --git a/third_party/blink/common/permissions_policy/origin_with_possible_wildcards.cc b/third_party/blink/common/permissions_policy/origin_with_possible_wildcards.cc
index c72ebe4..6c1de97 100644
--- a/third_party/blink/common/permissions_policy/origin_with_possible_wildcards.cc
+++ b/third_party/blink/common/permissions_policy/origin_with_possible_wildcards.cc
@@ -18,27 +18,6 @@
 OriginWithPossibleWildcards::OriginWithPossibleWildcards() = default;
 
 OriginWithPossibleWildcards::OriginWithPossibleWildcards(
-    const url::Origin& origin,
-    bool has_subdomain_wildcard) {
-  // Origins cannot be opaque.
-  DCHECK(!origin.opaque());
-  csp_source.scheme = origin.scheme();
-  csp_source.host = origin.host();
-  csp_source.port = origin.port() ?: url::PORT_UNSPECIFIED;
-  // Prevent url::Origin from writing the default port into the CSPSource
-  // as the normal parsing route doesn't do this.
-  if (csp_source.port == 80 && (csp_source.scheme == url::kHttpScheme ||
-                                csp_source.scheme == url::kWsScheme)) {
-    csp_source.port = url::PORT_UNSPECIFIED;
-  } else if (csp_source.port == 443 &&
-             (csp_source.scheme == url::kHttpsScheme ||
-              csp_source.scheme == url::kWssScheme)) {
-    csp_source.port = url::PORT_UNSPECIFIED;
-  }
-  csp_source.is_host_wildcard = has_subdomain_wildcard;
-}
-
-OriginWithPossibleWildcards::OriginWithPossibleWildcards(
     const OriginWithPossibleWildcards& rhs) = default;
 
 OriginWithPossibleWildcards& OriginWithPossibleWildcards::operator=(
@@ -47,17 +26,28 @@
 OriginWithPossibleWildcards::~OriginWithPossibleWildcards() = default;
 
 // static
-OriginWithPossibleWildcards OriginWithPossibleWildcards::FromOrigin(
-    const url::Origin& origin) {
-  return OriginWithPossibleWildcards(origin, /*has_subdomain_wildcard=*/false);
+absl::optional<OriginWithPossibleWildcards>
+OriginWithPossibleWildcards::FromOrigin(const url::Origin& origin) {
+  // Origins cannot be opaque.
+  if (origin.opaque()) {
+    return absl::nullopt;
+  }
+  return Parse(origin.Serialize(), NodeType::kHeader);
 }
 
 // static
-OriginWithPossibleWildcards
+absl::optional<OriginWithPossibleWildcards>
 OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
     const url::Origin& origin,
     bool has_subdomain_wildcard) {
-  return OriginWithPossibleWildcards(origin, has_subdomain_wildcard);
+  absl::optional<OriginWithPossibleWildcards> origin_with_possible_wildcards =
+      FromOrigin(origin);
+  if (origin_with_possible_wildcards.has_value()) {
+    // Overwrite wildcard settings.
+    origin_with_possible_wildcards->csp_source.is_host_wildcard =
+        has_subdomain_wildcard;
+  }
+  return origin_with_possible_wildcards;
 }
 
 // static
diff --git a/third_party/blink/common/permissions_policy/origin_with_possible_wildcards_unittest.cc b/third_party/blink/common/permissions_policy/origin_with_possible_wildcards_unittest.cc
index 4a19e2f..9ff8ca2 100644
--- a/third_party/blink/common/permissions_policy/origin_with_possible_wildcards_unittest.cc
+++ b/third_party/blink/common/permissions_policy/origin_with_possible_wildcards_unittest.cc
@@ -347,6 +347,8 @@
                       "Origin with just scheme"),
       std::make_tuple("https://192.168.0.1", true, "IPv4"),
       std::make_tuple("file://example.com", true, "File host"),
+      std::make_tuple("https://[2001:db8::1]", false, "IPv6"),
+      std::make_tuple("file:///test", false, "File path"),
   };
   for (const auto& value : values) {
     const auto& original = OriginWithPossibleWildcards::Parse(
@@ -365,32 +367,11 @@
   }
 }
 
-TEST_P(OriginWithPossibleWildcardsTest, SpecialOriginTypes) {
-  // Tuple of {serialized value, description}.
-  const auto& values = {
-      std::make_tuple("https://[2001:db8::1]", "IPv6"),
-      std::make_tuple("file:///test", "File path"),
-  };
-  for (const auto& value : values) {
-    const auto& original = OriginWithPossibleWildcards::FromOrigin(
-        url::Origin::Create(GURL(std::get<0>(value))));
-    SCOPED_TRACE(std::get<1>(value));
-    OriginWithPossibleWildcards copy;
-    EXPECT_NE(original, copy);
-    EXPECT_TRUE(
-        mojo::test::SerializeAndDeserialize<mojom::OriginWithPossibleWildcards>(
-            original, copy));
-    EXPECT_EQ(original, copy);
-  }
-}
-
 TEST_P(OriginWithPossibleWildcardsTest, Opaque) {
-  EXPECT_DCHECK_DEATH(
-      OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(url::Origin(),
-                                                                 true));
-  EXPECT_DCHECK_DEATH(
-      OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(url::Origin(),
-                                                                 false));
+  EXPECT_FALSE(OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+      url::Origin(), true));
+  EXPECT_FALSE(OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+      url::Origin(), false));
   OriginWithPossibleWildcards original;
   OriginWithPossibleWildcards copy;
   EXPECT_FALSE(
diff --git a/third_party/blink/common/permissions_policy/permissions_policy.cc b/third_party/blink/common/permissions_policy/permissions_policy.cc
index 1a8ee7a4..c044331 100644
--- a/third_party/blink/common/permissions_policy/permissions_policy.cc
+++ b/third_party/blink/common/permissions_policy/permissions_policy.cc
@@ -254,8 +254,12 @@
   if (default_policy == PermissionsPolicyFeatureDefault::EnableForAll) {
     default_allowlist.AddAll();
   } else if (default_policy == PermissionsPolicyFeatureDefault::EnableForSelf) {
-    default_allowlist.Add(
-        blink::OriginWithPossibleWildcards::FromOrigin(origin_));
+    absl::optional<blink::OriginWithPossibleWildcards>
+        origin_with_possible_wildcards =
+            blink::OriginWithPossibleWildcards::FromOrigin(origin_);
+    if (origin_with_possible_wildcards.has_value()) {
+      default_allowlist.Add(*origin_with_possible_wildcards);
+    }
   }
 
   return default_allowlist;
diff --git a/third_party/blink/common/permissions_policy/permissions_policy_declaration_unittest.cc b/third_party/blink/common/permissions_policy/permissions_policy_declaration_unittest.cc
index 4bbe2cb..0595c81 100644
--- a/third_party/blink/common/permissions_policy/permissions_policy_declaration_unittest.cc
+++ b/third_party/blink/common/permissions_policy/permissions_policy_declaration_unittest.cc
@@ -36,7 +36,7 @@
   // Origin mismatch.
   ParsedPermissionsPolicyDeclaration mismatch_decl;
   mismatch_decl.allowed_origins.emplace_back(
-      OriginWithPossibleWildcards::FromOrigin(
+      *OriginWithPossibleWildcards::FromOrigin(
           url::Origin::Create(GURL("https://example2.test/"))));
   EXPECT_FALSE(mismatch_decl.Contains(kTestOrigin));
   EXPECT_FALSE(mismatch_decl.Contains(kOpaqueOrigin));
@@ -44,7 +44,7 @@
   // Origin match.
   ParsedPermissionsPolicyDeclaration match_decl;
   match_decl.allowed_origins.emplace_back(
-      OriginWithPossibleWildcards::FromOrigin(
+      *OriginWithPossibleWildcards::FromOrigin(
           url::Origin::Create(GURL("https://example.test/"))));
   EXPECT_TRUE(match_decl.Contains(kTestOrigin));
   EXPECT_FALSE(match_decl.Contains(kOpaqueOrigin));
diff --git a/third_party/blink/common/permissions_policy/permissions_policy_unittest.cc b/third_party/blink/common/permissions_policy/permissions_policy_unittest.cc
index 4d46639..be18339 100644
--- a/third_party/blink/common/permissions_policy/permissions_policy_unittest.cc
+++ b/third_party/blink/common/permissions_policy/permissions_policy_unittest.cc
@@ -297,7 +297,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -336,7 +336,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -344,7 +344,7 @@
          /*matches_opaque_src=*/false}}});
   ParsedPermissionsPolicy frame_policy = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -521,7 +521,7 @@
                               /*matches_opaque_src=*/false}}});
   ParsedPermissionsPolicy frame_policy = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -554,7 +554,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultOnFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -590,7 +590,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultOnFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -626,7 +626,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -663,7 +663,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -814,7 +814,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy({{
       {kDefaultOnFeature, /*allowed_origins=*/
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_b_,
            /*has_subdomain_wildcard=*/false)},
        /*self_if_matches=*/origin_a_,
@@ -852,7 +852,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -893,7 +893,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -906,7 +906,7 @@
          /*matches_opaque_src=*/false}}});
   ParsedPermissionsPolicy frame_policy = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/origin_a_,
@@ -920,7 +920,7 @@
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy, origin_b_);
   ParsedPermissionsPolicy frame_policy2 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_c_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/origin_a_,
@@ -961,7 +961,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedPermissionsPolicy frame_policy = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -1035,7 +1035,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedPermissionsPolicy frame_policy1 = {{
       {kDefaultSelfFeature, /*allowed_origins=*/
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_b_,
            /*has_subdomain_wildcard=*/false)},
        /*self_if_matches=*/absl::nullopt,
@@ -1046,7 +1046,7 @@
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedPermissionsPolicy frame_policy2 = {{
       {kDefaultSelfFeature, /*allowed_origins=*/
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_c_,
            /*has_subdomain_wildcard=*/false)},
        /*self_if_matches=*/absl::nullopt,
@@ -1144,7 +1144,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy({{
       {kDefaultSelfFeature, /*allowed_origins=*/
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_b_,
            /*has_subdomain_wildcard=*/false)},
        /*self_if_matches=*/origin_a_,
@@ -1207,7 +1207,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedPermissionsPolicy frame_policy1 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -1264,7 +1264,7 @@
   }});
   ParsedPermissionsPolicy frame_policy1 = {{
       {kDefaultSelfFeature, /*allowed_origins=*/
-       {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_b_,
            /*has_subdomain_wildcard=*/false)},
        /*self_if_matches=*/absl::nullopt,
@@ -1317,7 +1317,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -1325,7 +1325,7 @@
          /*matches_opaque_src=*/false}}});
   ParsedPermissionsPolicy frame_policy1 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_a_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -1335,7 +1335,7 @@
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedPermissionsPolicy frame_policy2 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -1948,14 +1948,14 @@
     policy->SetHeaderPolicy(
         {{{mojom::PermissionsPolicyFeature::
                kBrowsingTopics, /*allowed_origins=*/
-           {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+           {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
                origin_b_,
                /*has_subdomain_wildcard=*/false)},
            /*self_if_matches=*/absl::nullopt,
            /*matches_all_origins=*/false,
            /*matches_opaque_src=*/false},
           {mojom::PermissionsPolicyFeature::kSharedStorage, /*allowed_origins=*/
-           {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+           {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
                origin_b_,
                /*has_subdomain_wildcard=*/false)},
            /*self_if_matches=*/absl::nullopt,
@@ -2157,7 +2157,7 @@
         CreateFromParentPolicy(nullptr, origin_a_);
     policy->SetHeaderPolicy(
         {{{mojom::PermissionsPolicyFeature::kSharedStorage, /*allowed_origins=*/
-           {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+           {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
                origin_b_,
                /*has_subdomain_wildcard=*/false)},
            /*self_if_matches=*/absl::nullopt,
@@ -2273,7 +2273,7 @@
 
   ParsedPermissionsPolicy frame_policy5 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2285,7 +2285,7 @@
 
   ParsedPermissionsPolicy frame_policy6 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_c_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2352,7 +2352,7 @@
   // This is a critical change from the existing semantics.
   ParsedPermissionsPolicy frame_policy5 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2364,7 +2364,7 @@
 
   ParsedPermissionsPolicy frame_policy6 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_c_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2408,7 +2408,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -2435,7 +2435,7 @@
 
   ParsedPermissionsPolicy frame_policy5 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2447,7 +2447,7 @@
 
   ParsedPermissionsPolicy frame_policy6 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_c_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2514,7 +2514,7 @@
 
   ParsedPermissionsPolicy frame_policy5 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2526,7 +2526,7 @@
 
   ParsedPermissionsPolicy frame_policy6 = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_c_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2558,7 +2558,7 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/origin_a_,
@@ -2631,10 +2631,10 @@
 TEST_F(PermissionsPolicyTest, CreateFromParsedPolicy) {
   ParsedPermissionsPolicy parsed_policy = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false),
-         blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         *blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2650,7 +2650,7 @@
 TEST_F(PermissionsPolicyTest, CreateFromParsedPolicyExcludingSelf) {
   ParsedPermissionsPolicy parsed_policy = {
       {{kDefaultSelfFeature, /*allowed_origins=*/
-        {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+        {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
             origin_b_,
             /*has_subdomain_wildcard=*/false)},
         /*self_if_matches=*/absl::nullopt,
@@ -2678,7 +2678,7 @@
   auto policy1 = CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2691,7 +2691,7 @@
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultSelfFeature));
   EXPECT_DCHECK_DEATH(policy2->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2702,7 +2702,7 @@
   auto policy3 = CreateFromParentPolicy(nullptr, origin_a_);
   policy3->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2710,7 +2710,7 @@
          /*matches_opaque_src=*/false}}});
   EXPECT_DCHECK_DEATH(policy3->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2733,7 +2733,7 @@
   policy1->SetHeaderPolicy(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2742,7 +2742,7 @@
   policy1->OverwriteHeaderPolicyForClientHints(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2756,7 +2756,7 @@
   policy2->SetHeaderPolicy(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2765,7 +2765,7 @@
   policy2->OverwriteHeaderPolicyForClientHints(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2778,7 +2778,7 @@
   auto policy3 = CreateFromParentPolicy(nullptr, origin_a_);
   policy3->SetHeaderPolicy(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_b_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2787,7 +2787,7 @@
   policy3->OverwriteHeaderPolicyForClientHints(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2800,7 +2800,7 @@
   auto policy4 = CreateFromParentPolicy(nullptr, origin_a_);
   EXPECT_DCHECK_DEATH(policy4->OverwriteHeaderPolicyForClientHints(
       {{{kDefaultSelfFeature, /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2812,7 +2812,7 @@
   policy5->SetHeaderPolicy(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2823,7 +2823,7 @@
   EXPECT_DCHECK_DEATH(policy5->OverwriteHeaderPolicyForClientHints(
       {{{mojom::PermissionsPolicyFeature::kClientHintDPR,
          /*allowed_origins=*/
-         {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+         {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
              origin_a_,
              /*has_subdomain_wildcard=*/false)},
          /*self_if_matches=*/absl::nullopt,
@@ -2835,7 +2835,7 @@
   // If we set a policy, then we can extract it.
   auto policy1 = CreateFromParentPolicy(nullptr, origin_a_);
   const std::vector<blink::OriginWithPossibleWildcards> origins1(
-      {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+      {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
           origin_b_,
           /*has_subdomain_wildcard=*/false)});
   policy1->SetHeaderPolicy({{{mojom::PermissionsPolicyFeature::kClientHintDPR,
@@ -2859,7 +2859,7 @@
   // If we set a policy, then overwrite it, we can extract it.
   auto policy3 = CreateFromParentPolicy(nullptr, origin_a_);
   const std::vector<blink::OriginWithPossibleWildcards> origins3(
-      {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+      {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
           origin_a_,
           /*has_subdomain_wildcard=*/false)});
   policy3->SetHeaderPolicy({{{mojom::PermissionsPolicyFeature::kClientHintDPR,
@@ -2883,10 +2883,10 @@
   // If we don't set a policy, then overwrite it, we can extract it.
   auto policy4 = CreateFromParentPolicy(nullptr, origin_a_);
   const std::vector<blink::OriginWithPossibleWildcards> origins4(
-      {blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+      {*blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_a_,
            /*has_subdomain_wildcard=*/false),
-       blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
+       *blink::OriginWithPossibleWildcards::FromOriginAndWildcardsForTest(
            origin_b_,
            /*has_subdomain_wildcard=*/false)});
   policy4->OverwriteHeaderPolicyForClientHints(
diff --git a/third_party/blink/public/common/input/web_gesture_event.h b/third_party/blink/public/common/input/web_gesture_event.h
index 889e607..a255b5ce 100644
--- a/third_party/blink/public/common/input/web_gesture_event.h
+++ b/third_party/blink/public/common/input/web_gesture_event.h
@@ -58,6 +58,7 @@
     } tap;
 
     struct {
+      int tap_down_count;
       float width;
       float height;
     } tap_down;
@@ -246,6 +247,7 @@
 
   gfx::SizeF TapAreaInRootFrame() const;
   int TapCount() const;
+  int TapDownCount() const;
 
   void ApplyTouchAdjustment(const gfx::PointF& root_frame_coords);
 
diff --git a/third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h b/third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h
index f61ef58..988d58e9 100644
--- a/third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h
+++ b/third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h
@@ -31,16 +31,18 @@
       const OriginWithPossibleWildcards& rhs);
   ~OriginWithPossibleWildcards();
 
-  // This is a shortcut for making a wildcard-free OriginWithPossibleWildcards
-  // from a non-opaque origin.
-  static OriginWithPossibleWildcards FromOrigin(const url::Origin& origin);
+  // This is a shortcut for making a wildcard-free OriginWithPossibleWildcards.
+  // The returned value will be empty if the origin is opaque or Parse fails.
+  static absl::optional<OriginWithPossibleWildcards> FromOrigin(
+      const url::Origin& origin);
 
-  // This is a shortcut for making a wildcard-free OriginWithPossibleWildcards
-  // from a non-opaque origin. Non-tests should use Parse() if they want
+  // This is a shortcut for making a OriginWithPossibleWildcards while
+  // overriding the wildcard options. Non-tests should use Parse() if they want
   // wildcard support, or FromOrigin() if they don't need it.
-  static OriginWithPossibleWildcards FromOriginAndWildcardsForTest(
-      const url::Origin& origin,
-      bool has_subdomain_wildcard);
+  // The returned value will be empty if the origin is opaque or Parse fails.
+  static absl::optional<OriginWithPossibleWildcards>
+  FromOriginAndWildcardsForTest(const url::Origin& origin,
+                                bool has_subdomain_wildcard);
 
   // This constructs a OriginWithPossibleWildcards from an allowlist_entry which
   // might or might not have a subdomain wildcard (only if the type is kHeader).
@@ -87,9 +89,6 @@
   friend bool operator<(const OriginWithPossibleWildcards& lhs,
                         const OriginWithPossibleWildcards& rhs);
 
-  OriginWithPossibleWildcards(const url::Origin& origin,
-                              bool has_subdomain_wildcard);
-
   network::mojom::CSPSource csp_source;
 };
 
diff --git a/third_party/blink/public/mojom/input/input_handler.mojom b/third_party/blink/public/mojom/input/input_handler.mojom
index 57e752fc..ac4d743 100644
--- a/third_party/blink/public/mojom/input/input_handler.mojom
+++ b/third_party/blink/public/mojom/input/input_handler.mojom
@@ -131,6 +131,10 @@
   bool needs_wheel_event;
 };
 
+struct TapDownData {
+  int32 tap_down_count;
+};
+
 struct GestureData {
   gfx.mojom.PointF screen_position;
   gfx.mojom.PointF widget_position;
@@ -145,6 +149,7 @@
   PinchUpdateData? pinch_update_data;
   PinchEndData? pinch_end_data;
   TapData? tap_data;
+  TapDownData? tap_down_data;
   FlingData? fling_data;
 };
 
diff --git a/third_party/blink/public/mojom/smart_card/smart_card.mojom b/third_party/blink/public/mojom/smart_card/smart_card.mojom
index 5499d83..5d1629e 100644
--- a/third_party/blink/public/mojom/smart_card/smart_card.mojom
+++ b/third_party/blink/public/mojom/smart_card/smart_card.mojom
@@ -84,4 +84,7 @@
   Connect(string reader, device.mojom.SmartCardShareMode share_mode,
           device.mojom.SmartCardProtocols preferred_protocols) =>
     (device.mojom.SmartCardConnectResult result);
+
+  // Creates an Application Context to the PC/SC Resource Manager.
+  CreateContext() => (device.mojom.SmartCardCreateContextResult result);
 };
diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
index 52af66c..a9d8b17d 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
@@ -815,6 +815,7 @@
     kScrollStartTarget = 761,
     kTimelineScope = 762,
     kScrollbarColor = 763,
+    kWordBoundaryDetection = 764,
     // 1. Add new features above this line (don't change the assigned numbers of
     //    the existing items).
     // 2. Run the src/tools/metrics/histograms/update_use_counter_css.py script
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 a32a599..1954d6f 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
@@ -1242,7 +1242,7 @@
   kBaseWithDataHref = 1760,
   kBaseWithNewlinesInTarget = 1761,
   kBaseWithOpenBracketInTarget = 1762,
-  kBaseWouldBeBlockedByDefaultSrc = 1763,
+  kOBSOLETE_BaseWouldBeBlockedByDefaultSrc = 1763,
   kV8AssigmentExpressionLHSIsCallInSloppy = 1764,
   kV8AssigmentExpressionLHSIsCallInStrict = 1765,
   kV8PromiseConstructorReturnedUndefined = 1766,
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 5bc7d67..c0d51e5b 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -3022,10 +3022,18 @@
 # we need to compile in the same way as would happen when current_os="android".
 if (target_os != "android") {
   generated_dictionary_sources_in_modules += [
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connect_result.cc",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connect_result.h",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connection_status.cc",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connection_status.h",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_error_options.cc",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_error_options.h",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_flags.cc",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_flags.h",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_in.cc",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_in.h",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_out.cc",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_out.h",
   ]
   generated_enumeration_sources_in_modules += [
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_access_mode.cc",
@@ -3044,6 +3052,8 @@
   generated_interface_sources_in_modules += [
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connection.cc",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_connection.h",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_context.cc",
+    "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_context.h",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_error.cc",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_error.h",
     "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index b6386f6b..da408f7 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -1258,6 +1258,7 @@
             "//third_party/blink/renderer/modules/smart_card/navigator_smart_card.idl",
             "//third_party/blink/renderer/modules/smart_card/smart_card_connection.idl",
             "//third_party/blink/renderer/modules/smart_card/smart_card_connection_status.idl",
+            "//third_party/blink/renderer/modules/smart_card/smart_card_context.idl",
             "//third_party/blink/renderer/modules/smart_card/smart_card_error.idl",
             "//third_party/blink/renderer/modules/smart_card/smart_card_reader.idl",
             "//third_party/blink/renderer/modules/smart_card/smart_card_reader_presence_event.idl",
diff --git a/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
index 9f509421..fab4da4 100644
--- a/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/interpolable_length.h"
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -21,43 +22,23 @@
 
 class RayMode {
  public:
-  RayMode(StyleRay::RaySize size,
-          bool contain,
-          const BasicShapeCenterCoordinate& center_x,
-          const BasicShapeCenterCoordinate& center_y,
-          bool has_explicit_center)
-      : size_(size),
-        contain_(contain),
-        center_x_(center_x),
-        center_y_(center_y),
-        has_explicit_center_(has_explicit_center) {}
+  RayMode(StyleRay::RaySize size, bool contain)
+      : size_(size), contain_(contain) {}
 
   explicit RayMode(const StyleRay& style_ray)
-      : size_(style_ray.Size()),
-        contain_(style_ray.Contain()),
-        center_x_(style_ray.CenterX()),
-        center_y_(style_ray.CenterY()),
-        has_explicit_center_(style_ray.HasExplicitCenter()) {}
+      : size_(style_ray.Size()), contain_(style_ray.Contain()) {}
 
   StyleRay::RaySize Size() const { return size_; }
   bool Contain() const { return contain_; }
-  const BasicShapeCenterCoordinate& CenterX() const { return center_x_; }
-  const BasicShapeCenterCoordinate& CenterY() const { return center_y_; }
-  bool HasExplicitCenter() const { return has_explicit_center_; }
 
   bool operator==(const RayMode& other) const {
-    return size_ == other.size_ && contain_ == other.contain_ &&
-           center_x_ == other.center_x_ && center_y_ == other.center_y_ &&
-           has_explicit_center_ == other.has_explicit_center_;
+    return size_ == other.size_ && contain_ == other.contain_;
   }
   bool operator!=(const RayMode& other) const { return !(*this == other); }
 
  private:
   StyleRay::RaySize size_;
   bool contain_;
-  BasicShapeCenterCoordinate center_x_;
-  BasicShapeCenterCoordinate center_y_;
-  bool has_explicit_center_ = true;
 };
 
 }  // namespace
@@ -73,7 +54,7 @@
   DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
 
  private:
-  CSSRayNonInterpolableValue(const RayMode& mode) : mode_(mode) {}
+  explicit CSSRayNonInterpolableValue(const RayMode& mode) : mode_(mode) {}
 
   const RayMode mode_;
 };
@@ -121,7 +102,7 @@
 
 class InheritedRayChecker : public CSSInterpolationType::CSSConversionChecker {
  public:
-  InheritedRayChecker(scoped_refptr<const StyleRay> style_ray)
+  explicit InheritedRayChecker(scoped_refptr<const StyleRay> style_ray)
       : style_ray_(std::move(style_ray)) {
     DCHECK(style_ray_);
   }
@@ -135,8 +116,53 @@
   scoped_refptr<const StyleRay> style_ray_;
 };
 
-InterpolationValue CreateValue(float angle, const RayMode& mode) {
-  return InterpolationValue(std::make_unique<InterpolableNumber>(angle),
+std::unique_ptr<InterpolableValue> ConvertCoordinate(
+    const BasicShapeCenterCoordinate& coordinate,
+    double zoom) {
+  return InterpolableLength::MaybeConvertLength(coordinate.ComputedLength(),
+                                                zoom);
+}
+
+std::unique_ptr<InterpolableValue> CreateNeutralInterpolableCoordinate() {
+  return InterpolableLength::CreateNeutral();
+}
+
+BasicShapeCenterCoordinate CreateCoordinate(
+    const InterpolableValue& interpolable_value,
+    const CSSToLengthConversionData& conversion_data) {
+  return BasicShapeCenterCoordinate(
+      BasicShapeCenterCoordinate::kTopLeft,
+      To<InterpolableLength>(interpolable_value)
+          .CreateLength(conversion_data, Length::ValueRange::kAll));
+}
+
+enum RayComponentIndex : unsigned {
+  kRayAngleIndex,
+  kRayCenterXIndex,
+  kRayCenterYIndex,
+  kRayHasExplicitCenterIndex,
+  kRayComponentIndexCount,
+};
+
+InterpolationValue CreateValue(const StyleRay& ray, double zoom) {
+  auto list = std::make_unique<InterpolableList>(kRayComponentIndexCount);
+  list->Set(kRayAngleIndex, std::make_unique<InterpolableNumber>(ray.Angle()));
+  list->Set(kRayCenterXIndex, ConvertCoordinate(ray.CenterX(), zoom));
+  list->Set(kRayCenterYIndex, ConvertCoordinate(ray.CenterY(), zoom));
+  list->Set(kRayHasExplicitCenterIndex,
+            std::make_unique<InterpolableNumber>(ray.HasExplicitCenter()));
+  return InterpolationValue(std::move(list),
+                            CSSRayNonInterpolableValue::Create(RayMode(ray)));
+}
+
+InterpolationValue CreateNeutralValue(const RayMode& mode) {
+  auto list = std::make_unique<InterpolableList>(kRayComponentIndexCount);
+  list->Set(kRayAngleIndex, std::make_unique<InterpolableNumber>(0));
+  list->Set(kRayCenterXIndex, CreateNeutralInterpolableCoordinate());
+  list->Set(kRayCenterYIndex, CreateNeutralInterpolableCoordinate());
+  list->Set(kRayHasExplicitCenterIndex,
+            std::make_unique<InterpolableNumber>(0));
+  return InterpolationValue(std::move(list),
                             CSSRayNonInterpolableValue::Create(mode));
 }
 
@@ -148,14 +174,16 @@
     StyleResolverState& state) const {
   const auto& ray_non_interpolable_value =
       To<CSSRayNonInterpolableValue>(*non_interpolable_value);
-  // TODO(sakhapov): make position interpolable.
-  scoped_refptr<StyleRay> style_ray =
-      StyleRay::Create(To<InterpolableNumber>(interpolable_value).Value(),
-                       ray_non_interpolable_value.Mode().Size(),
-                       ray_non_interpolable_value.Mode().Contain(),
-                       ray_non_interpolable_value.Mode().CenterX(),
-                       ray_non_interpolable_value.Mode().CenterY(),
-                       ray_non_interpolable_value.Mode().HasExplicitCenter());
+  const auto& list = To<InterpolableList>(interpolable_value);
+  scoped_refptr<StyleRay> style_ray = StyleRay::Create(
+      To<InterpolableNumber>(list.Get(kRayAngleIndex))->Value(),
+      ray_non_interpolable_value.Mode().Size(),
+      ray_non_interpolable_value.Mode().Contain(),
+      CreateCoordinate(*list.Get(kRayCenterXIndex),
+                       state.CssToLengthConversionData()),
+      CreateCoordinate(*list.Get(kRayCenterYIndex),
+                       state.CssToLengthConversionData()),
+      To<InterpolableNumber>(list.Get(kRayHasExplicitCenterIndex))->Value());
   // TODO(sakhapov): handle coord box.
   state.StyleBuilder().SetOffsetPath(
       ShapeOffsetPathOperation::Create(style_ray, CoordBox::kBorderBox));
@@ -187,7 +215,7 @@
       To<CSSRayNonInterpolableValue>(*underlying.non_interpolable_value).Mode();
   conversion_checkers.push_back(
       std::make_unique<UnderlyingRayModeChecker>(underlying_mode));
-  return CreateValue(0, underlying_mode);
+  return CreateNeutralValue(underlying_mode);
 }
 
 InterpolationValue CSSRayInterpolationType::MaybeConvertInitial(
@@ -209,7 +237,7 @@
 
   conversion_checkers.push_back(
       std::make_unique<InheritedRayChecker>(inherited_ray));
-  return CreateValue(inherited_ray->Angle(), RayMode(*inherited_ray));
+  return CreateValue(*inherited_ray, state.ParentStyle()->EffectiveZoom());
 }
 
 PairwiseInterpolationValue CSSRayInterpolationType::MaybeMergeSingles(
@@ -233,7 +261,7 @@
   if (!underlying_ray)
     return nullptr;
 
-  return CreateValue(underlying_ray->Angle(), RayMode(*underlying_ray));
+  return CreateValue(*underlying_ray, style.EffectiveZoom());
 }
 
 InterpolationValue CSSRayInterpolationType::MaybeConvertValue(
@@ -252,8 +280,8 @@
   if (!shape) {
     return nullptr;
   }
-  return CreateValue(To<StyleRay>(*shape).Angle(),
-                     RayMode(To<StyleRay>(*shape)));
+  return CreateValue(To<StyleRay>(*shape),
+                     state->ParentStyle()->EffectiveZoom());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index 2ff5dce..fb18006 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -55,6 +55,7 @@
   "container_query_evaluator.h",
   "container_selector.cc",
   "container_selector.h",
+  "container_stuck.h",
   "style_containment_scope.cc",
   "style_containment_scope.h",
   "style_containment_scope_tree.cc",
@@ -707,6 +708,7 @@
   "vision_deficiency.cc",
   "vision_deficiency.h",
   "white_space.h",
+  "word_boundary_detection.h",
   "zoom_adjusted_pixel_value.h",
 ]
 
@@ -723,6 +725,7 @@
   "counter_style_map_test.cc",
   "counter_style_test.cc",
   "css_computed_style_declaration_test.cc",
+  "css_container_values_test.cc",
   "css_font_face_source_test.cc",
   "css_font_family_webkit_prefix_test.cc",
   "css_gradient_value_test.cc",
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator.cc b/third_party/blink/renderer/core/css/container_query_evaluator.cc
index 408f6ec..c441757 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/container_query_evaluator.cc
@@ -101,7 +101,8 @@
 
 ContainerQueryEvaluator::ContainerQueryEvaluator(Element& container) {
   auto* query_values = MakeGarbageCollected<CSSContainerValues>(
-      container.GetDocument(), container, absl::nullopt, absl::nullopt);
+      container.GetDocument(), container, absl::nullopt, absl::nullopt,
+      ContainerStuckPhysical::kNo, ContainerStuckPhysical::kNo);
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -267,6 +268,23 @@
   return change;
 }
 
+ContainerQueryEvaluator::Change ContainerQueryEvaluator::StickyContainerChanged(
+    ContainerStuckPhysical stuck_horizontal,
+    ContainerStuckPhysical stuck_vertical) {
+  if (stuck_horizontal_ == stuck_horizontal &&
+      stuck_vertical_ == stuck_vertical) {
+    return Change::kNone;
+  }
+
+  UpdateContainerStuck(stuck_horizontal, stuck_vertical);
+  Change change = ComputeStickyChange();
+  if (change != Change::kNone) {
+    ClearResults(change, kStickyContainer);
+  }
+
+  return change;
+}
+
 ContainerQueryEvaluator::Change
 ContainerQueryEvaluator::StyleContainerChanged() {
   if (!depends_on_style_) {
@@ -295,8 +313,8 @@
   absl::optional<double> width;
   absl::optional<double> height;
 
-  Element* container =
-      media_query_evaluator_->GetMediaValues().ContainerElement();
+  const MediaValues& existing_values = media_query_evaluator_->GetMediaValues();
+  Element* container = existing_values.ContainerElement();
 
   // An axis is "supported" only when it appears in the computed value of
   // 'container-type', and when containment is actually applied for that axis.
@@ -316,7 +334,24 @@
   }
 
   auto* query_values = MakeGarbageCollected<CSSContainerValues>(
-      container->GetDocument(), *container, width, height);
+      container->GetDocument(), *container, width, height,
+      existing_values.StuckHorizontal(), existing_values.StuckVertical());
+  media_query_evaluator_ =
+      MakeGarbageCollected<MediaQueryEvaluator>(query_values);
+}
+
+void ContainerQueryEvaluator::UpdateContainerStuck(
+    ContainerStuckPhysical stuck_horizontal,
+    ContainerStuckPhysical stuck_vertical) {
+  stuck_horizontal_ = stuck_horizontal;
+  stuck_vertical_ = stuck_vertical;
+
+  const MediaValues& existing_values = media_query_evaluator_->GetMediaValues();
+  Element* container = existing_values.ContainerElement();
+
+  auto* query_values = MakeGarbageCollected<CSSContainerValues>(
+      container->GetDocument(), *container, existing_values.Width(),
+      existing_values.Height(), stuck_horizontal, stuck_vertical);
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -340,6 +375,8 @@
     if (pair.value.change <= change &&
         ((container_type == kSizeContainer &&
           pair.key->Selector().SelectsSizeContainers()) ||
+         (container_type == kStickyContainer &&
+          pair.key->Selector().SelectsStickyContainers()) ||
          (container_type == kStyleContainer &&
           pair.key->Selector().SelectsStyleContainers()))) {
       continue;
@@ -390,6 +427,24 @@
   return change;
 }
 
+ContainerQueryEvaluator::Change ContainerQueryEvaluator::ComputeStickyChange()
+    const {
+  Change change = Change::kNone;
+
+  for (const auto& result : results_) {
+    const ContainerQuery& query = *result.key;
+    if (!query.Selector().SelectsStickyContainers()) {
+      continue;
+    }
+    if (Eval(query).value == result.value.value) {
+      continue;
+    }
+    change = std::max(result.value.change, change);
+  }
+
+  return change;
+}
+
 void ContainerQueryEvaluator::UpdateContainerValuesFromUnitChanges(
     StyleRecalcChange change) {
   CHECK(media_query_evaluator_);
@@ -410,7 +465,8 @@
   Element* container = existing_values.ContainerElement();
   auto* query_values = MakeGarbageCollected<CSSContainerValues>(
       container->GetDocument(), *container, existing_values.Width(),
-      existing_values.Height());
+      existing_values.Height(), existing_values.StuckHorizontal(),
+      existing_values.StuckVertical());
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator.h b/third_party/blink/renderer/core/css/container_query_evaluator.h
index b43c6a4c..48aad9a 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator.h
+++ b/third_party/blink/renderer/core/css/container_query_evaluator.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/container_selector.h"
+#include "third_party/blink/renderer/core/css/container_stuck.h"
 #include "third_party/blink/renderer/core/css/media_query_evaluator.h"
 #include "third_party/blink/renderer/core/css/media_query_exp.h"
 #include "third_party/blink/renderer/core/css/style_recalc_change.h"
@@ -74,6 +75,11 @@
   // Re-evaluate the cached results and clear any results which are affected.
   Change StyleContainerChanged();
 
+  // Re-evaluate the cached results and clear any results which are affected by
+  // the ContainerStuckPhysical changes.
+  Change StickyContainerChanged(ContainerStuckPhysical stuck_horizontal,
+                                ContainerStuckPhysical stuck_vertical);
+
   // We may need to update the internal CSSContainerValues of this evaluator
   // when e.g. the rem unit changes.
   void UpdateContainerValuesFromUnitChanges(StyleRecalcChange);
@@ -96,7 +102,11 @@
   // used for queries.
   void UpdateContainerSize(PhysicalSize, PhysicalAxes contained_axes);
 
-  enum ContainerType { kSizeContainer, kStyleContainer };
+  // Update the CSSContainerValues with the new stuck state.
+  void UpdateContainerStuck(ContainerStuckPhysical stuck_horizontal,
+                            ContainerStuckPhysical stuck_vertical);
+
+  enum ContainerType { kSizeContainer, kStyleContainer, kStickyContainer };
   void ClearResults(Change change, ContainerType container_type);
 
   // Re-evaluate cached query results after a size change and return which
@@ -106,6 +116,7 @@
   // Re-evaluate cached query results after a style change and return which
   // elements need to be invalidated if necessary.
   Change ComputeStyleChange() const;
+  Change ComputeStickyChange() const;
 
   struct Result {
     // Main evaluation result.
@@ -130,6 +141,8 @@
   Member<MediaQueryEvaluator> media_query_evaluator_;
   PhysicalSize size_;
   PhysicalAxes contained_axes_;
+  ContainerStuckPhysical stuck_horizontal_ = ContainerStuckPhysical::kNo;
+  ContainerStuckPhysical stuck_vertical_ = ContainerStuckPhysical::kNo;
   HeapHashMap<Member<const ContainerQuery>, Result> results_;
   // The MediaQueryExpValue::UnitFlags of all queries evaluated against this
   // ContainerQueryEvaluator.
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
index a1c96ccfc..d8af88c9 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
@@ -119,6 +119,17 @@
     return evaluator->SizeContainerChanged(size, axes);
   }
 
+  Change StickyContainerChanged(ContainerQueryEvaluator* evaluator,
+                                ContainerStuckPhysical stuck_horizontal,
+                                ContainerStuckPhysical stuck_vertical,
+                                unsigned container_type) {
+    ComputedStyleBuilder builder(
+        *GetDocument().GetStyleResolver().InitialStyleForElement());
+    builder.SetContainerType(container_type);
+    ContainerElement().SetComputedStyle(builder.TakeStyle());
+    return evaluator->StickyContainerChanged(stuck_horizontal, stuck_vertical);
+  }
+
   bool EvalAndAdd(ContainerQueryEvaluator* evaluator,
                   const ContainerQuery& query,
                   Change change = Change::kNearestContainer) {
@@ -156,6 +167,7 @@
   const unsigned type_normal = kContainerTypeNormal;
   const unsigned type_size = kContainerTypeSize;
   const unsigned type_inline_size = kContainerTypeInlineSize;
+  const unsigned type_sticky = kContainerTypeSticky;
 };
 
 TEST_F(ContainerQueryEvaluatorTest, ContainmentMatch) {
@@ -347,6 +359,46 @@
   EXPECT_EQ(0u, GetResults(evaluator).size());
 }
 
+TEST_F(ContainerQueryEvaluatorTest, StickyContainerChanged) {
+  ContainerQuery* container_query_left = ParseContainer("state(stuck: left)");
+  ContainerQuery* container_query_bottom =
+      ParseContainer("state(stuck: bottom)");
+  ASSERT_TRUE(container_query_left);
+  ASSERT_TRUE(container_query_bottom);
+
+  ContainerQueryEvaluator* evaluator = CreateEvaluatorForType(type_sticky);
+  StickyContainerChanged(evaluator, ContainerStuckPhysical::kLeft,
+                         ContainerStuckPhysical::kNo, type_sticky);
+
+  EXPECT_TRUE(EvalAndAdd(evaluator, *container_query_left));
+  EXPECT_FALSE(EvalAndAdd(evaluator, *container_query_bottom));
+  EXPECT_EQ(2u, GetResults(evaluator).size());
+
+  // Calling StickyContainerChanged the values we already have should not
+  // produce a Change.
+  EXPECT_EQ(Change::kNone,
+            StickyContainerChanged(evaluator, ContainerStuckPhysical::kLeft,
+                                   ContainerStuckPhysical::kNo, type_sticky));
+  EXPECT_EQ(2u, GetResults(evaluator).size());
+
+  // EvalAndAdding the same queries again is allowed.
+  EXPECT_TRUE(EvalAndAdd(evaluator, *container_query_left));
+  EXPECT_FALSE(EvalAndAdd(evaluator, *container_query_bottom));
+  EXPECT_EQ(2u, GetResults(evaluator).size());
+
+  // Set vertically stuck to bottom.
+  EXPECT_EQ(
+      Change::kNearestContainer,
+      StickyContainerChanged(evaluator, ContainerStuckPhysical::kLeft,
+                             ContainerStuckPhysical::kBottom, type_sticky));
+  EXPECT_EQ(0u, GetResults(evaluator).size());
+
+  // Now both left and bottom queries should return true.
+  EXPECT_TRUE(EvalAndAdd(evaluator, *container_query_left));
+  EXPECT_TRUE(EvalAndAdd(evaluator, *container_query_bottom));
+  EXPECT_EQ(2u, GetResults(evaluator).size());
+}
+
 TEST_F(ContainerQueryEvaluatorTest, ClearResults) {
   PhysicalSize size_100(LayoutUnit(100), LayoutUnit(100));
 
diff --git a/third_party/blink/renderer/core/css/container_stuck.h b/third_party/blink/renderer/core/css/container_stuck.h
new file mode 100644
index 0000000..b534262
--- /dev/null
+++ b/third_party/blink/renderer/core/css/container_stuck.h
@@ -0,0 +1,39 @@
+// 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_CONTAINER_STUCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CONTAINER_STUCK_H_
+
+// Enum classes that represents whether a sticky positioned element is stuck to
+// a scroll container edge for a given axis. Used for evaluating stuck state
+// container queries.
+enum class ContainerStuckLogical {
+  // Not stuck
+  kNo,
+  // Stuck to inset-inline-start, or inset-block-start
+  kStart,
+  // Stuck to inset-inline-end, or inset-block-end
+  kEnd,
+};
+
+enum class ContainerStuckPhysical {
+  kNo,
+  kLeft,
+  kRight,
+  kTop,
+  kBottom,
+};
+
+inline ContainerStuckLogical Flip(ContainerStuckLogical stuck) {
+  switch (stuck) {
+    case ContainerStuckLogical::kNo:
+      return ContainerStuckLogical::kNo;
+    case ContainerStuckLogical::kStart:
+      return ContainerStuckLogical::kEnd;
+    case ContainerStuckLogical::kEnd:
+      return ContainerStuckLogical::kStart;
+  }
+}
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CONTAINER_STUCK_H_
diff --git a/third_party/blink/renderer/core/css/css_container_values.cc b/third_party/blink/renderer/core/css/css_container_values.cc
index cc90e4b..f995ccc 100644
--- a/third_party/blink/renderer/core/css/css_container_values.cc
+++ b/third_party/blink/renderer/core/css/css_container_values.cc
@@ -13,12 +13,16 @@
 CSSContainerValues::CSSContainerValues(Document& document,
                                        Element& container,
                                        absl::optional<double> width,
-                                       absl::optional<double> height)
+                                       absl::optional<double> height,
+                                       ContainerStuckPhysical stuck_horizontal,
+                                       ContainerStuckPhysical stuck_vertical)
     : MediaValuesDynamic(document.GetFrame()),
       element_(&container),
       width_(width),
       height_(height),
-      writing_mode_(container.ComputedStyleRef().GetWritingMode()),
+      writing_direction_(container.ComputedStyleRef().GetWritingDirection()),
+      stuck_horizontal_(stuck_horizontal),
+      stuck_vertical_(stuck_vertical),
       font_sizes_(CSSToLengthConversionData::FontSizes(
           container.ComputedStyleRef().GetFontSizeStyle(),
           document.documentElement()->GetComputedStyle())),
@@ -81,4 +85,48 @@
   return container_sizes_.Height().value_or(SmallViewportHeight());
 }
 
+namespace {
+
+// Converts from left/right/top/bottom to start/end as if the writing mode and
+// direction was horizontal-tb and ltr.
+ContainerStuckLogical PhysicalToLogicalLtrHorizontalTb(
+    ContainerStuckPhysical physical) {
+  switch (physical) {
+    case ContainerStuckPhysical::kNo:
+      return ContainerStuckLogical::kNo;
+    case ContainerStuckPhysical::kLeft:
+    case ContainerStuckPhysical::kTop:
+      return ContainerStuckLogical::kStart;
+    case ContainerStuckPhysical::kRight:
+    case ContainerStuckPhysical::kBottom:
+      return ContainerStuckLogical::kEnd;
+  }
+}
+
+}  // namespace
+
+ContainerStuckLogical CSSContainerValues::StuckInline() const {
+  // TODO(crbug.com/1445189): The WritingDirection should be taken from the
+  // container's containing block, not the container. Otherwise the inset
+  // properties on the sticky positioned will not match the same inset features
+  // in container queries when writing-mode or direction changes on the sticky
+  // positioned itself.
+  ContainerStuckPhysical physical =
+      writing_direction_.IsHorizontal() ? StuckHorizontal() : StuckVertical();
+  ContainerStuckLogical logical = PhysicalToLogicalLtrHorizontalTb(physical);
+  return writing_direction_.IsRtl() ? Flip(logical) : logical;
+}
+
+ContainerStuckLogical CSSContainerValues::StuckBlock() const {
+  // TODO(crbug.com/1445189): The WritingDirection should be taken from the
+  // container's containing block, not the container. Otherwise the inset
+  // properties on the sticky positioned will not match the same inset features
+  // in container queries when writing-mode or direction changes on the sticky
+  // positioned itself.
+  ContainerStuckPhysical physical =
+      writing_direction_.IsHorizontal() ? StuckVertical() : StuckHorizontal();
+  ContainerStuckLogical logical = PhysicalToLogicalLtrHorizontalTb(physical);
+  return writing_direction_.IsFlippedBlocks() ? Flip(logical) : logical;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_container_values.h b/third_party/blink/renderer/core/css/css_container_values.h
index 8db1cea..ab9fa63 100644
--- a/third_party/blink/renderer/core/css/css_container_values.h
+++ b/third_party/blink/renderer/core/css/css_container_values.h
@@ -11,12 +11,14 @@
 
 namespace blink {
 
-class CSSContainerValues : public MediaValuesDynamic {
+class CORE_EXPORT CSSContainerValues : public MediaValuesDynamic {
  public:
   explicit CSSContainerValues(Document& document,
                               Element& container,
                               absl::optional<double> width,
-                              absl::optional<double> height);
+                              absl::optional<double> height,
+                              ContainerStuckPhysical stuck_horizontal,
+                              ContainerStuckPhysical stuck_vertical);
 
   // Returns absl::nullopt if queries on the relevant axis is not
   // supported.
@@ -41,7 +43,17 @@
   Element* ContainerElement() const override { return element_; }
   double ContainerWidth() const override;
   double ContainerHeight() const override;
-  WritingMode GetWritingMode() const override { return writing_mode_; }
+  WritingMode GetWritingMode() const override {
+    return writing_direction_.GetWritingMode();
+  }
+  ContainerStuckPhysical StuckHorizontal() const override {
+    return stuck_horizontal_;
+  }
+  ContainerStuckPhysical StuckVertical() const override {
+    return stuck_vertical_;
+  }
+  ContainerStuckLogical StuckInline() const override;
+  ContainerStuckLogical StuckBlock() const override;
 
  private:
   // The current computed style for the container.
@@ -51,7 +63,11 @@
   // Container height in CSS pixels.
   absl::optional<double> height_;
   // The writing-mode of the container.
-  WritingMode writing_mode_;
+  WritingDirectionMode writing_direction_;
+  // Whether a sticky container is horizontally stuck and to which edge.
+  ContainerStuckPhysical stuck_horizontal_ = ContainerStuckPhysical::kNo;
+  // Whether a sticky container is vertically stuck and against which edge.
+  ContainerStuckPhysical stuck_vertical_ = ContainerStuckPhysical::kNo;
   // Container font sizes for resolving relative lengths.
   CSSToLengthConversionData::FontSizes font_sizes_;
   // LineHeightSize of the container element.
diff --git a/third_party/blink/renderer/core/css/css_container_values_test.cc b/third_party/blink/renderer/core/css/css_container_values_test.cc
new file mode 100644
index 0000000..86104ef
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_container_values_test.cc
@@ -0,0 +1,93 @@
+// 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_container_values.h"
+
+#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+
+namespace blink {
+
+class CSSContainerValuesTest : public PageTestBase {
+ public:
+  void SetUp() override {
+    PageTestBase::SetUp();
+    GetDocument().body()->setInnerHTML(R"HTML(
+      <div id="container"></div>
+    )HTML");
+  }
+
+  void SetContainerWritingDirection(WritingMode writing_mode,
+                                    TextDirection direction) {
+    ComputedStyleBuilder builder(
+        *GetDocument().GetStyleResolver().InitialStyleForElement());
+    builder.SetWritingMode(writing_mode);
+    builder.SetDirection(direction);
+    ContainerElement().SetComputedStyle(builder.TakeStyle());
+  }
+
+  CSSContainerValues* CreateStickyValues(ContainerStuckPhysical horizontal,
+                                         ContainerStuckPhysical vertical) {
+    return MakeGarbageCollected<CSSContainerValues>(
+        GetDocument(), ContainerElement(), absl::nullopt, absl::nullopt,
+        horizontal, vertical);
+  }
+
+ private:
+  Element& ContainerElement() {
+    return *GetDocument().getElementById("container");
+  }
+};
+
+TEST_F(CSSContainerValuesTest, StickyHorizontalTbLtr) {
+  SetContainerWritingDirection(WritingMode::kHorizontalTb, TextDirection::kLtr);
+  MediaValues* values = CreateStickyValues(ContainerStuckPhysical::kRight,
+                                           ContainerStuckPhysical::kTop);
+  EXPECT_EQ(values->StuckInline(), ContainerStuckLogical::kEnd);
+  EXPECT_EQ(values->StuckBlock(), ContainerStuckLogical::kStart);
+}
+
+TEST_F(CSSContainerValuesTest, StickyHorizontalTbRtl) {
+  SetContainerWritingDirection(WritingMode::kHorizontalTb, TextDirection::kRtl);
+  MediaValues* values = CreateStickyValues(ContainerStuckPhysical::kRight,
+                                           ContainerStuckPhysical::kTop);
+  EXPECT_EQ(values->StuckInline(), ContainerStuckLogical::kStart);
+  EXPECT_EQ(values->StuckBlock(), ContainerStuckLogical::kStart);
+}
+
+TEST_F(CSSContainerValuesTest, StickyVerticalLrLtr) {
+  SetContainerWritingDirection(WritingMode::kVerticalLr, TextDirection::kLtr);
+  MediaValues* values = CreateStickyValues(ContainerStuckPhysical::kRight,
+                                           ContainerStuckPhysical::kTop);
+  EXPECT_EQ(values->StuckInline(), ContainerStuckLogical::kStart);
+  EXPECT_EQ(values->StuckBlock(), ContainerStuckLogical::kEnd);
+}
+
+TEST_F(CSSContainerValuesTest, StickyVerticalLrRtl) {
+  SetContainerWritingDirection(WritingMode::kVerticalLr, TextDirection::kRtl);
+  MediaValues* values = CreateStickyValues(ContainerStuckPhysical::kRight,
+                                           ContainerStuckPhysical::kTop);
+  EXPECT_EQ(values->StuckInline(), ContainerStuckLogical::kEnd);
+  EXPECT_EQ(values->StuckBlock(), ContainerStuckLogical::kEnd);
+}
+
+TEST_F(CSSContainerValuesTest, StickyVerticalRlLtr) {
+  SetContainerWritingDirection(WritingMode::kVerticalRl, TextDirection::kLtr);
+  MediaValues* values = CreateStickyValues(ContainerStuckPhysical::kRight,
+                                           ContainerStuckPhysical::kTop);
+  EXPECT_EQ(values->StuckInline(), ContainerStuckLogical::kStart);
+  EXPECT_EQ(values->StuckBlock(), ContainerStuckLogical::kStart);
+}
+
+TEST_F(CSSContainerValuesTest, StickyVerticalRlRtl) {
+  SetContainerWritingDirection(WritingMode::kVerticalRl, TextDirection::kRtl);
+  MediaValues* values = CreateStickyValues(ContainerStuckPhysical::kRight,
+                                           ContainerStuckPhysical::kTop);
+  EXPECT_EQ(values->StuckInline(), ContainerStuckLogical::kEnd);
+  EXPECT_EQ(values->StuckBlock(), ContainerStuckLogical::kStart);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 5195fa1..01279dd 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -6196,6 +6196,21 @@
       typedom_types: ["Keyword"],
     },
     {
+      name: "word-boundary-detection",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
+      style_builder_custom_functions: ["value"],
+      inherited: true,
+      field_group: "*",
+      field_template: "primitive",
+      type_name: "WordBoundaryDetection",
+      field_size: 1,
+      include_paths: ["third_party/blink/renderer/core/css/word_boundary_detection.h"],
+      keywords: ["normal"],
+      default_value: "WordBoundaryDetection::kNormal",
+      typedom_types: ["Keyword"],
+      runtime_flag: "CSSWordBoundaryDetection",
+    },
+    {
       name: "word-break",
       property_methods: ["CSSValueFromComputedStyleInternal"],
       inherited: true,
diff --git a/third_party/blink/renderer/core/css/css_properties_ranking.json5 b/third_party/blink/renderer/core/css/css_properties_ranking.json5
index 93cd3d26..6c38423 100644
--- a/third_party/blink/renderer/core/css/css_properties_ranking.json5
+++ b/third_party/blink/renderer/core/css/css_properties_ranking.json5
@@ -106,6 +106,7 @@
         "text-shadow",
         "border-bottom-right-radius",
         "word-break",
+        "word-boundary-detection",
         "clip",
         "border-top-right-radius",
         "flex-grow",
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index 23cb60a..a16eed4 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -877,6 +877,8 @@
       return a.AccentColor() == b.AccentColor();
     case CSSPropertyID::kTextEmphasisColor:
       return a.TextEmphasisColor() == b.TextEmphasisColor();
+    case CSSPropertyID::kWordBoundaryDetection:
+      return a.GetWordBoundaryDetection() == b.GetWordBoundaryDetection();
     case CSSPropertyID::kZoom:
       return a.Zoom() == b.Zoom();
 
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index 3b07262..1f6c6fe 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -790,6 +790,11 @@
     // right
 
     //
+    // word-boundary-detection
+    //
+    "ja",
+
+    //
     // word-break
     //
     "break-all",
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index a5fc3eda..8ae9a06 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -1437,8 +1437,35 @@
 static bool StuckMediaFeatureEval(const MediaQueryExpValue& value,
                                   MediaQueryOperator op,
                                   const MediaValues& media_values) {
-  // TODO(crbug.com/1445189): Implement matching.
-  return false;
+  if (!value.IsValid()) {
+    return media_values.StuckHorizontal() != ContainerStuckPhysical::kNo ||
+           media_values.StuckVertical() != ContainerStuckPhysical::kNo;
+  }
+
+  switch (value.Id()) {
+    case CSSValueID::kNone:
+      return media_values.StuckHorizontal() == ContainerStuckPhysical::kNo &&
+             media_values.StuckVertical() == ContainerStuckPhysical::kNo;
+    case CSSValueID::kTop:
+      return media_values.StuckVertical() == ContainerStuckPhysical::kTop;
+    case CSSValueID::kLeft:
+      return media_values.StuckHorizontal() == ContainerStuckPhysical::kLeft;
+    case CSSValueID::kBottom:
+      return media_values.StuckVertical() == ContainerStuckPhysical::kBottom;
+    case CSSValueID::kRight:
+      return media_values.StuckHorizontal() == ContainerStuckPhysical::kRight;
+    case CSSValueID::kInsetBlockStart:
+      return media_values.StuckBlock() == ContainerStuckLogical::kStart;
+    case CSSValueID::kInsetBlockEnd:
+      return media_values.StuckBlock() == ContainerStuckLogical::kEnd;
+    case CSSValueID::kInsetInlineStart:
+      return media_values.StuckInline() == ContainerStuckLogical::kStart;
+    case CSSValueID::kInsetInlineEnd:
+      return media_values.StuckInline() == ContainerStuckLogical::kEnd;
+    default:
+      NOTREACHED();
+      return false;
+  }
 }
 
 static MediaQueryOperator ReverseOperator(MediaQueryOperator op) {
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index c2e8501..daa9ceb 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -12,9 +12,11 @@
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/container_stuck.h"
 #include "third_party/blink/renderer/core/css/css_length_resolver.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
 #include "ui/base/pointer/pointer_device.h"
 
@@ -89,6 +91,24 @@
   virtual int GetHorizontalViewportSegments() const = 0;
   virtual int GetVerticalViewportSegments() const = 0;
   virtual device::mojom::blink::DevicePostureType GetDevicePosture() const = 0;
+  // For evaluating state(stuck: left), state(stuck: right)
+  virtual ContainerStuckPhysical StuckHorizontal() const {
+    return ContainerStuckPhysical::kNo;
+  }
+  // For evaluating state(stuck: top), state(stuck: bottom)
+  virtual ContainerStuckPhysical StuckVertical() const {
+    return ContainerStuckPhysical::kNo;
+  }
+  // For evaluating state(stuck: inset-inline-start),
+  // state(stuck: inset-inline-end)
+  virtual ContainerStuckLogical StuckInline() const {
+    return ContainerStuckLogical::kNo;
+  }
+  // For evaluating state(stuck: inset-block-start),
+  // state(stuck: inset-block-end)
+  virtual ContainerStuckLogical StuckBlock() const {
+    return ContainerStuckLogical::kNo;
+  }
   // Returns the container element used to retrieve base style and parent style
   // when computing the computed value of a style() container query.
   virtual Element* ContainerElement() const { return nullptr; }
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 14923edc..5286f21f3 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
@@ -7263,6 +7263,32 @@
                        UnitlessQuirk::kAllow);
 }
 
+CSSFunctionValue* CreateWordBoundaryDetectionValue() {
+  CSSFunctionValue* function =
+      MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kAuto);
+  function->Append(*CSSIdentifierValue::Create(CSSValueID::kJa));
+  return function;
+}
+
+CSSValue* ParseWordBoundaryDetection(CSSParserTokenRange& range,
+                                     const CSSParserContext& context) {
+  if (CSSValue* ident = ConsumeIdent<CSSValueID::kNormal>(range)) {
+    return ident;
+  }
+
+  // Parse `auto(ja)`.
+  if (range.Peek().FunctionId() == CSSValueID::kAuto) {
+    CSSParserTokenRange block = range.ConsumeBlock();
+    const CSSParserToken& lang = block.Consume();
+    if (lang.Id() == CSSValueID::kJa && block.AtEnd()) {
+      return CreateWordBoundaryDetectionValue();
+    }
+    // If the `lang` is not supported, make the declaration invalid.
+    // https://drafts.csswg.org/css-text-4/#valdef-word-boundary-detection-auto-lang
+  }
+  return nullptr;
+}
+
 CSSValue* ConsumeSingleContainerName(CSSParserTokenRange& range,
                                      const CSSParserContext& context) {
   if (range.Peek().GetType() != kIdentToken) {
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 85466b4..e88b65b1 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
@@ -538,6 +538,9 @@
                              UnitlessQuirk);
 CSSValue* ConsumeSVGPaint(CSSParserTokenRange&, const CSSParserContext&);
 CSSValue* ParseSpacing(CSSParserTokenRange&, const CSSParserContext&);
+CSSFunctionValue* CreateWordBoundaryDetectionValue();
+CSSValue* ParseWordBoundaryDetection(CSSParserTokenRange&,
+                                     const CSSParserContext&);
 
 CSSValue* ConsumeSingleContainerName(CSSParserTokenRange&,
                                      const CSSParserContext&);
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index caf0fb9..c5ff09f 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -10351,6 +10351,45 @@
       will_change_contents || state.ParentStyle()->SubtreeWillChangeContents());
 }
 
+const CSSValue* WordBoundaryDetection::ParseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ParseWordBoundaryDetection(range, context);
+}
+
+const CSSValue* WordBoundaryDetection::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style) const {
+  switch (style.GetWordBoundaryDetection()) {
+    case blink::WordBoundaryDetection::kNormal:
+      return CSSIdentifierValue::Create(CSSValueID::kNormal);
+    case blink::WordBoundaryDetection::kAuto:
+      return css_parsing_utils::CreateWordBoundaryDetectionValue();
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+void WordBoundaryDetection::ApplyValue(StyleResolverState& state,
+                                       const CSSValue& value,
+                                       ValueMode) const {
+  if (value.IsIdentifierValue()) {
+    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNormal);
+    state.StyleBuilder().SetWordBoundaryDetection(
+        ComputedStyleInitialValues::InitialWordBoundaryDetection());
+    return;
+  }
+  if (value.IsFunctionValue()) {
+    DCHECK_EQ(To<CSSFunctionValue>(value).FunctionType(), CSSValueID::kAuto);
+    state.StyleBuilder().SetWordBoundaryDetection(
+        blink::WordBoundaryDetection::kAuto);
+    return;
+  }
+  NOTREACHED();
+}
+
 const CSSValue* WordBreak::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
     const LayoutObject*,
diff --git a/third_party/blink/renderer/core/css/word_boundary_detection.h b/third_party/blink/renderer/core/css/word_boundary_detection.h
new file mode 100644
index 0000000..53291f8
--- /dev/null
+++ b/third_party/blink/renderer/core/css/word_boundary_detection.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_WORD_BOUNDARY_DETECTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_WORD_BOUNDARY_DETECTION_H_
+
+namespace blink {
+
+// Values of the `word-boundary-detection` property.
+// https://drafts.csswg.org/css-text-4/#word-boundary-detection
+enum class WordBoundaryDetection {
+  kNormal,
+  kAuto,
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_WORD_BOUNDARY_DETECTION_H_
diff --git a/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc b/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc
index 07a21e9..5cbc2c0 100644
--- a/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc
+++ b/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc
@@ -162,12 +162,18 @@
       *root_editable_element, expanded_selection.ComputeRange());
   String input_text = input_method_controller.TextInputInfo().value;
   if (expanded_range.length() > 2 &&
-      IsHTMLSpace(input_text[expanded_range.Start()]) &&
-      IsHTMLSpace(input_text[expanded_range.End() - 1])) {
+      IsHTMLSpaceNotLineBreak(input_text[expanded_range.Start()]) &&
+      IsHTMLSpaceNotLineBreak(input_text[expanded_range.End() - 1])) {
     // Special case, we don't want to delete spaces both sides of the
     // selection as that will join words together.
     return PlainTextRange(expanded_range.Start() + 1, expanded_range.End());
   }
+  if (input_text.length() > expanded_range.End() + 1 &&
+      expanded_range.Start() - 1 >= 0 &&
+      IsHTMLSpaceNotLineBreak(input_text[expanded_range.Start() - 1]) &&
+      IsHTMLSpaceNotLineBreak(input_text[expanded_range.End()])) {
+    return PlainTextRange(expanded_range.Start() - 1, expanded_range.End());
+  }
   return expanded_range;
 }
 
diff --git a/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture_test.cc b/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture_test.cc
index a926056..a1c0058 100644
--- a/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture_test.cc
+++ b/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture_test.cc
@@ -216,7 +216,8 @@
       // Crossing out inside a word without crossing over the middle should not
       // affect the word.
       TestCase(0, 24, "ABCDEFG", "ABCDEFG"),
-  };
+      // Deleting a word with spaces either side removes one space.
+      TestCase(32, 45, "AB CDE FGH", "AB FGH")};
   for (auto test_case : test_cases) {
     input->SetValue(test_case.initial);
     const int width = input->BoundsInWidget().width();
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 2acb243..12f5fee 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -273,6 +273,15 @@
                        const std::string& html_file);
   bool SimulateGestureAtElement(WebInputEvent::Type, Element*);
   bool SimulateGestureAtElementById(WebInputEvent::Type, const WebString& id);
+  WebGestureEvent BuildTapEvent(WebInputEvent::Type,
+                                int tap_event_count,
+                                const gfx::PointF& position_in_widget);
+  bool SimulateTapEventAtElement(WebInputEvent::Type,
+                                 int tap_event_count,
+                                 Element*);
+  bool SimulateTapEventAtElementById(WebInputEvent::Type,
+                                     int tap_event_count,
+                                     const WebString& id);
   gfx::Size PrintICBSizeFromPageSize(const gfx::Size& page_size);
 
   ExternalDateTimeChooser* GetExternalDateTimeChooser(
@@ -2760,6 +2769,61 @@
   return SimulateGestureAtElement(type, element);
 }
 
+WebGestureEvent WebViewTest::BuildTapEvent(
+    WebInputEvent::Type type,
+    int tap_event_count,
+    const gfx::PointF& position_in_widget) {
+  WebGestureEvent event(type, WebInputEvent::kNoModifiers,
+                        WebInputEvent::GetStaticTimeStampForTests(),
+                        WebGestureDevice::kTouchscreen);
+  event.SetPositionInWidget(position_in_widget);
+
+  switch (type) {
+    case WebInputEvent::Type::kGestureTapDown:
+      event.data.tap_down.tap_down_count = tap_event_count;
+      break;
+    case WebInputEvent::Type::kGestureTap:
+      event.data.tap.tap_count = tap_event_count;
+      break;
+    default:
+      break;
+  }
+  return event;
+}
+
+bool WebViewTest::SimulateTapEventAtElement(WebInputEvent::Type type,
+                                            int tap_event_count,
+                                            Element* element) {
+  if (!element || !element->GetLayoutObject()) {
+    return false;
+  }
+
+  DCHECK(web_view_helper_.GetWebView());
+  element->scrollIntoViewIfNeeded();
+
+  const gfx::PointF center = gfx::PointF(
+      web_view_helper_.GetWebView()
+          ->MainFrameImpl()
+          ->GetFrameView()
+          ->FrameToScreen(element->GetLayoutObject()->AbsoluteBoundingBoxRect())
+          .CenterPoint());
+
+  const WebGestureEvent event = BuildTapEvent(type, tap_event_count, center);
+  web_view_helper_.GetWebView()->MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(event, ui::LatencyInfo()));
+  RunPendingTasks();
+  return true;
+}
+
+bool WebViewTest::SimulateTapEventAtElementById(WebInputEvent::Type type,
+                                                int tap_event_count,
+                                                const WebString& id) {
+  DCHECK(web_view_helper_.GetWebView());
+  auto* element = static_cast<Element*>(
+      web_view_helper_.LocalMainFrame()->GetDocument().GetElementById(id));
+  return SimulateTapEventAtElement(type, tap_event_count, element);
+}
+
 gfx::Size WebViewTest::PrintICBSizeFromPageSize(const gfx::Size& page_size) {
   // The expected layout size comes from the calculation done in
   // ResizePageRectsKeepingRatio() which is used from PrintContext::begin() to
@@ -3237,6 +3301,92 @@
   EXPECT_EQ("testword", frame->SelectionAsText().Utf8());
 }
 
+TEST_F(WebViewTest, DoublePressSelection) {
+  ScopedTouchTextEditingRedesignForTest touch_text_editing_redesign(true);
+  RegisterMockedHttpURLLoad("double_press_selection.html");
+
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + "double_press_selection.html");
+  web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
+  UpdateAllLifecyclePhases();
+  RunPendingTasks();
+
+  WebString target = WebString::FromUTF8("target");
+  WebLocalFrameImpl* frame = web_view->MainFrameImpl();
+
+  // Double press should select nearest word.
+  EXPECT_TRUE(SimulateTapEventAtElementById(
+      WebInputEvent::Type::kGestureTapDown, 1, target));
+  EXPECT_TRUE(SimulateTapEventAtElementById(WebInputEvent::Type::kGestureTap, 1,
+                                            target));
+  EXPECT_TRUE(SimulateTapEventAtElementById(
+      WebInputEvent::Type::kGestureTapDown, 2, target));
+  EXPECT_EQ("selection", frame->SelectionAsText().Utf8());
+
+  // Releasing double tap should keep the selection.
+  EXPECT_TRUE(SimulateTapEventAtElementById(WebInputEvent::Type::kGestureTap, 2,
+                                            target));
+  EXPECT_EQ("selection", frame->SelectionAsText().Utf8());
+}
+
+TEST_F(WebViewTest, DoublePressSelectionOnSelectStartFalse) {
+  ScopedTouchTextEditingRedesignForTest touch_text_editing_redesign(true);
+  RegisterMockedHttpURLLoad("double_press_selection.html");
+
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + "double_press_selection.html");
+  web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
+  UpdateAllLifecyclePhases();
+  RunPendingTasks();
+
+  WebString onselectstartfalse = WebString::FromUTF8("onselectstartfalse");
+  WebLocalFrameImpl* frame = web_view->MainFrameImpl();
+
+  // Should not select anything when onselectstart is false.
+  EXPECT_TRUE(SimulateTapEventAtElementById(
+      WebInputEvent::Type::kGestureTapDown, 1, onselectstartfalse));
+  EXPECT_TRUE(SimulateTapEventAtElementById(WebInputEvent::Type::kGestureTap, 1,
+                                            onselectstartfalse));
+  EXPECT_TRUE(SimulateTapEventAtElementById(
+      WebInputEvent::Type::kGestureTapDown, 2, onselectstartfalse));
+  EXPECT_EQ("", frame->SelectionAsText().Utf8());
+  EXPECT_TRUE(SimulateTapEventAtElementById(WebInputEvent::Type::kGestureTap, 2,
+                                            onselectstartfalse));
+  EXPECT_EQ("", frame->SelectionAsText().Utf8());
+}
+
+TEST_F(WebViewTest, DoublePressSelectionPreventDefaultMouseDown) {
+  ScopedTouchTextEditingRedesignForTest touch_text_editing_redesign(true);
+  RegisterMockedHttpURLLoad("double_press_selection.html");
+
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + "double_press_selection.html");
+  web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
+  UpdateAllLifecyclePhases();
+  RunPendingTasks();
+
+  web_view->MainFrameImpl()->ExecuteScript(
+      WebScriptSource("document.getElementById('targetdiv').addEventListener("
+                      "'mousedown', function(e) { e.preventDefault();});"));
+
+  WebString target = WebString::FromUTF8("target");
+  WebLocalFrameImpl* frame = web_view->MainFrameImpl();
+
+  // Double press should not select anything.
+  EXPECT_TRUE(SimulateTapEventAtElementById(
+      WebInputEvent::Type::kGestureTapDown, 1, target));
+  EXPECT_TRUE(SimulateTapEventAtElementById(WebInputEvent::Type::kGestureTap, 1,
+                                            target));
+  EXPECT_TRUE(SimulateTapEventAtElementById(
+      WebInputEvent::Type::kGestureTapDown, 2, target));
+  EXPECT_EQ("", frame->SelectionAsText().Utf8());
+
+  // Releasing double tap also should not select anything.
+  EXPECT_TRUE(SimulateTapEventAtElementById(WebInputEvent::Type::kGestureTap, 2,
+                                            target));
+  EXPECT_EQ("", frame->SelectionAsText().Utf8());
+}
+
 TEST_F(WebViewTest, FinishComposingTextDoesNotDismissHandles) {
   RegisterMockedHttpURLLoad("longpress_selection.html");
 
diff --git a/third_party/blink/renderer/core/fetch/body_stream_buffer.cc b/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
index ae4750a27..d922854 100644
--- a/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
+++ b/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h"
 #include "third_party/blink/renderer/core/streams/readable_byte_stream_controller.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/readable_stream_byob_request.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_with_script_scope.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
@@ -543,15 +544,27 @@
     if (result == BytesConsumer::Result::kShouldWait)
       return;
     DOMUint8Array* array = nullptr;
+    DOMArrayBufferView* byob_view = nullptr;
     if (result == BytesConsumer::Result::kOk) {
-      // TODO(nidhijaju): Follow the algorithm in
-      // https://streams.spec.whatwg.org/#readablestream-enqueue, i.e. don't
-      // create a new array if there is a current BYOB request view.
-      array = DOMUint8Array::CreateOrNull(
-          reinterpret_cast<const unsigned char*>(buffer),
-          base::checked_cast<uint32_t>(available));
+      if (stream_->GetController()->IsByteStreamController()) {
+        auto* byte_controller =
+            To<ReadableByteStreamController>(stream_->GetController());
+        if (ReadableStreamBYOBRequest* request =
+                byte_controller->byobRequest()) {
+          DOMArrayBufferView* view = request->view().Get();
+          available = std::min(view->byteLength(), available);
+          memcpy(view->buffer()->Data(), buffer, available);
+          byob_view = view;
+        }
+      }
+      if (!byob_view) {
+        CHECK(!array);
+        array = DOMUint8Array::CreateOrNull(
+            reinterpret_cast<const unsigned char*>(buffer),
+            base::checked_cast<uint32_t>(available));
+      }
       result = consumer_->EndRead(available);
-      if (!array) {
+      if (!array && !byob_view) {
         RaiseOOMError();
         return;
       }
@@ -559,7 +572,7 @@
     switch (result) {
       case BytesConsumer::Result::kOk:
       case BytesConsumer::Result::kDone:
-        if (array) {
+        if (array || byob_view) {
           // Clear |stream_needs_more_| in order to detect a pull call.
           stream_needs_more_ = false;
           if (underlying_byte_source_) {
@@ -569,15 +582,22 @@
             ExceptionState exception_state(script_state_->GetIsolate(),
                                            ExceptionState::kUnknownContext, "",
                                            "");
-            ReadableByteStreamController::Enqueue(
-                script_state_, byte_controller, NotShared(array),
-                exception_state);
+            if (byob_view) {
+              ReadableByteStreamController::Respond(
+                  script_state_, byte_controller, available, exception_state);
+            } else {
+              CHECK(array);
+              ReadableByteStreamController::Enqueue(
+                  script_state_, byte_controller, NotShared(array),
+                  exception_state);
+            }
             if (exception_state.HadException()) {
               exception_state.ClearException();
               return;
             }
           } else {
             CHECK(underlying_source_);
+            CHECK(array);
             underlying_source_->Controller()->Enqueue(array);
           }
         }
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index 7a9c5d3..a588d41 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -92,18 +92,30 @@
   kContentSecurityPolicyHashAlgorithmSha512 = 1 << 4
 };
 
-// Helper function that returns true if the given |header_type| should be
-// checked when the CheckHeaderType is |check_header_type|.
+// Returns true if the given `header_type` should be checked given
+// `check_header_type` and `reporting_disposition`.
 bool CheckHeaderTypeMatches(
     ContentSecurityPolicy::CheckHeaderType check_header_type,
+    ReportingDisposition reporting_disposition,
     ContentSecurityPolicyType header_type) {
-  switch (check_header_type) {
-    case ContentSecurityPolicy::CheckHeaderType::kCheckAll:
-      return true;
-    case ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly:
-      return header_type == ContentSecurityPolicyType::kReport;
-    case ContentSecurityPolicy::CheckHeaderType::kCheckEnforce:
-      return header_type == ContentSecurityPolicyType::kEnforce;
+  switch (reporting_disposition) {
+    case ReportingDisposition::kSuppressReporting:
+      switch (check_header_type) {
+        case ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly:
+          return false;
+        case ContentSecurityPolicy::CheckHeaderType::kCheckAll:
+        case ContentSecurityPolicy::CheckHeaderType::kCheckEnforce:
+          return header_type == ContentSecurityPolicyType::kEnforce;
+      }
+    case ReportingDisposition::kReport:
+      switch (check_header_type) {
+        case ContentSecurityPolicy::CheckHeaderType::kCheckAll:
+          return true;
+        case ContentSecurityPolicy::CheckHeaderType::kCheckReportOnly:
+          return header_type == ContentSecurityPolicyType::kReport;
+        case ContentSecurityPolicy::CheckHeaderType::kCheckEnforce:
+          return header_type == ContentSecurityPolicyType::kEnforce;
+      }
   }
   NOTREACHED();
   return false;
@@ -623,8 +635,7 @@
     ParserDisposition parser_disposition,
     const KURL& url_before_redirects,
     RedirectStatus redirect_status,
-    ReportingDisposition reporting_disposition,
-    ContentSecurityPolicy::CheckHeaderType check_header_type) {
+    ReportingDisposition reporting_disposition) {
   // The loop ignores default-src directives, which is the directive to report
   // for resource hints. So we don't need to check report-only policies.
   if (csp.header->type == ContentSecurityPolicyType::kEnforce) {
@@ -682,10 +693,12 @@
     }
 
     return base::ranges::all_of(policies_, [&](const auto& policy) {
-      return AllowResourceHintRequestForPolicy(
-          *policy, this, url, nonce, integrity_metadata, parser_disposition,
-          url_before_redirects, redirect_status, reporting_disposition,
-          check_header_type);
+      return !CheckHeaderTypeMatches(check_header_type, reporting_disposition,
+                                     policy->header->type) ||
+             AllowResourceHintRequestForPolicy(
+                 *policy, this, url, nonce, integrity_metadata,
+                 parser_disposition, url_before_redirects, redirect_status,
+                 reporting_disposition);
     });
   }
 
@@ -749,8 +762,10 @@
 
   bool is_allowed = true;
   for (const auto& policy : policies_) {
-    if (!CheckHeaderTypeMatches(check_header_type, policy->header->type))
+    if (!CheckHeaderTypeMatches(check_header_type, reporting_disposition,
+                                policy->header->type)) {
       continue;
+    }
     is_allowed &= CSPDirectiveListAllowFromSource(
         *policy, this, type, url, url_before_redirects, redirect_status,
         reporting_disposition, nonce, hashes, parser_disposition);
@@ -837,6 +852,7 @@
   violation_details = AllowTrustedTypePolicyDetails::kAllowed;
   for (const auto& policy : policies_) {
     if (!CheckHeaderTypeMatches(CheckHeaderType::kCheckAll,
+                                ReportingDisposition::kReport,
                                 policy->header->type)) {
       continue;
     }
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index e4cfac8..a132e3b 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -876,19 +876,8 @@
   }
 
   CSPOperativeDirective directive = OperativeDirective(csp, type);
-  bool result =
-      CheckSource(csp, policy, directive, url, type, url_before_redirects,
-                  redirect_status, reporting_disposition);
-
-  if (type == CSPDirectiveName::BaseURI) {
-    if (result && !CheckSource(csp, policy, directive, url, type,
-                               url_before_redirects, redirect_status,
-                               ReportingDisposition::kSuppressReporting)) {
-      policy->Count(WebFeature::kBaseWouldBeBlockedByDefaultSrc);
-    }
-  }
-
-  return result;
+  return CheckSource(csp, policy, directive, url, type, url_before_redirects,
+                     redirect_status, reporting_disposition);
 }
 
 bool CSPDirectiveListAllowTrustedTypePolicy(
diff --git a/third_party/blink/renderer/core/frame/frame_types.h b/third_party/blink/renderer/core/frame/frame_types.h
index 7256c55..7b02351 100644
--- a/third_party/blink/renderer/core/frame/frame_types.h
+++ b/third_party/blink/renderer/core/frame/frame_types.h
@@ -32,21 +32,6 @@
 namespace blink {
 
 enum class ClientRedirectPolicy { kNotClientRedirect, kClientRedirect };
-
-// Used for tracing LayoutView::CalculateScrollbarModes.
-enum class ScrollbarDisableReason {
-  kAutosizeMode = 0,
-  kNullDomWindow,
-  kNullFrame,
-  kPaintPreview,
-  kPrinting,
-  kMainFrameClipsContentFalse,
-  kIframeScrollingNo,
-  kFrameSet,
-  kFrameViewCanHaveScrollbarsFalse,
-  kSVGRoot,
-  kOverflowHidden
-};
 }
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_TYPES_H_
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 16c8010..e49e7e8 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -2117,28 +2117,19 @@
   }
 }
 
-bool LocalFrame::ClipsContent(ScrollbarDisableReason* out_reason) const {
+bool LocalFrame::ClipsContent() const {
   // A paint preview shouldn't clip to the viewport. Each frame paints to a
   // separate canvas in full to allow scrolling.
   if (GetDocument()->GetPaintPreviewState() != Document::kNotPaintingPreview) {
-    if (out_reason) {
-      *out_reason = ScrollbarDisableReason::kPaintPreview;
-    }
     return false;
   }
 
   if (ShouldUsePrintingLayout()) {
-    if (out_reason) {
-      *out_reason = ScrollbarDisableReason::kPrinting;
-    }
     return false;
   }
 
-  if (IsOutermostMainFrame() && !GetSettings()->GetMainFrameClipsContent()) {
-    if (out_reason) {
-      *out_reason = ScrollbarDisableReason::kMainFrameClipsContentFalse;
-    }
-    return false;
+  if (IsOutermostMainFrame()) {
+    return GetSettings()->GetMainFrameClipsContent();
   }
   // By default clip to viewport.
   return true;
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 4e00ed3b..0cc1aae 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -666,7 +666,7 @@
   bool IsHidden() const { return hidden_; }
 
   // Whether the frame clips its content to the frame's size.
-  bool ClipsContent(ScrollbarDisableReason* out_reason = nullptr) const;
+  bool ClipsContent() const;
 
   // For a navigation initiated from this LocalFrame with user gesture, record
   // the UseCounter AdClickNavigation if this frame is an adframe.
diff --git a/third_party/blink/renderer/core/html/client_hints_util.cc b/third_party/blink/renderer/core/html/client_hints_util.cc
index b49779c..e044fc94 100644
--- a/third_party/blink/renderer/core/html/client_hints_util.cc
+++ b/third_party/blink/renderer/core/html/client_hints_util.cc
@@ -70,7 +70,11 @@
     std::set<blink::OriginWithPossibleWildcards> origin_set(
         allow_list.AllowedOrigins().begin(), allow_list.AllowedOrigins().end());
     for (const auto& origin : pair.second) {
-      origin_set.insert(blink::OriginWithPossibleWildcards::FromOrigin(origin));
+      if (auto origin_with_possible_wildcards =
+              blink::OriginWithPossibleWildcards::FromOrigin(origin);
+          origin_with_possible_wildcards.has_value()) {
+        origin_set.insert(*origin_with_possible_wildcards);
+      }
     }
     auto declaration = ParsedPermissionsPolicyDeclaration(
         policy_name,
diff --git a/third_party/blink/renderer/core/html/parser/html_parser_idioms.h b/third_party/blink/renderer/core/html/parser/html_parser_idioms.h
index aba85b58..3aaacdf 100644
--- a/third_party/blink/renderer/core/html/parser/html_parser_idioms.h
+++ b/third_party/blink/renderer/core/html/parser/html_parser_idioms.h
@@ -128,6 +128,11 @@
   return !IsHTMLSpace<CharType>(character);
 }
 
+template <typename CharType>
+inline bool IsHTMLSpaceNotLineBreak(CharType character) {
+  return IsHTMLSpace<CharType>(character) && !IsHTMLLineBreak(character);
+}
+
 bool ThreadSafeMatch(const QualifiedName&, const QualifiedName&);
 bool ThreadSafeMatch(const String&, const QualifiedName&);
 
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index f3f89be2..1ce7ceb 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -929,6 +929,85 @@
   ASSERT_TRUE(Selection().IsHandleVisible());
 }
 
+TEST_F(EventHandlerTest, SelectionOnDoublePress) {
+  ScopedTouchTextEditingRedesignForTest touch_text_editing_redesign(true);
+  SetHtmlInnerHTML(
+      R"HTML(
+        <div id='targetdiv' style='font-size:500%;width:50px;'>
+        <p id='target' contenteditable>Test selection</p>
+        </div>
+      )HTML");
+
+  Element* element = GetDocument().getElementById("target");
+  gfx::PointF tap_point = gfx::PointF(element->BoundsInWidget().CenterPoint());
+  TapDownEventBuilder single_tap_down_event(tap_point);
+  single_tap_down_event.data.tap_down.tap_down_count = 1;
+  TapEventBuilder single_tap_event(tap_point, 1);
+  TapDownEventBuilder double_tap_down_event(tap_point);
+  double_tap_down_event.data.tap_down.tap_down_count = 2;
+  TapEventBuilder double_tap_event(tap_point, 2);
+
+  // Double press should select nearest word.
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      single_tap_down_event);
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      single_tap_event);
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      double_tap_down_event);
+  EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsRange());
+  EXPECT_EQ(Selection().SelectedText(), "selection");
+
+  // Releasing double tap should keep the selection.
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      double_tap_event);
+  EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsRange());
+  EXPECT_EQ(Selection().SelectedText(), "selection");
+}
+
+TEST_F(EventHandlerTest, SelectionOnDoublePressPreventDefaultMousePress) {
+  ScopedTouchTextEditingRedesignForTest touch_text_editing_redesign(true);
+  GetDocument().GetSettings()->SetScriptEnabled(true);
+  SetHtmlInnerHTML(
+      R"HTML(
+        <div id='targetdiv' style='font-size:500%;width:50px;'>
+        <p id='target' contenteditable>Test selection</p>
+        </div>
+      )HTML");
+  Element* script = GetDocument().CreateRawElement(html_names::kScriptTag);
+  script->setInnerHTML(
+      R"HTML(
+        let targetDiv = document.getElementById('targetdiv');
+        targetDiv.addEventListener('mousedown', (e) => {
+          e.preventDefault();
+        });
+      )HTML");
+  GetDocument().body()->AppendChild(script);
+  GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
+
+  Element* element = GetDocument().getElementById("target");
+  gfx::PointF tap_point = gfx::PointF(element->BoundsInWidget().CenterPoint());
+  TapDownEventBuilder single_tap_down_event(tap_point);
+  single_tap_down_event.data.tap_down.tap_down_count = 1;
+  TapEventBuilder single_tap_event(tap_point, 1);
+  TapDownEventBuilder double_tap_down_event(tap_point);
+  double_tap_down_event.data.tap_down.tap_down_count = 2;
+  TapEventBuilder double_tap_event(tap_point, 2);
+
+  // Double press should not select anything.
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      single_tap_down_event);
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      single_tap_event);
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      double_tap_down_event);
+  EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsNone());
+
+  // Releasing double tap also should not select anything.
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(
+      double_tap_event);
+  EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsNone());
+}
+
 TEST_F(EventHandlerTest, ClearHandleAfterTap) {
   SetHtmlInnerHTML("<textarea cols=50  rows=10>Enter text</textarea>");
 
diff --git a/third_party/blink/renderer/core/input/gesture_manager.cc b/third_party/blink/renderer/core/input/gesture_manager.cc
index 091fbca..cd3d43d 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.cc
+++ b/third_party/blink/renderer/core/input/gesture_manager.cc
@@ -69,6 +69,7 @@
 
 void GestureManager::Clear() {
   suppress_mouse_events_from_gestures_ = false;
+  suppress_selection_on_repeated_tap_down_ = false;
   ResetLongTapContextMenuStates();
 }
 
@@ -186,9 +187,32 @@
 
 WebInputEventResult GestureManager::HandleGestureTapDown(
     const GestureEventWithHitTestResults& targeted_event) {
+  const WebGestureEvent& gesture_event = targeted_event.Event();
   suppress_mouse_events_from_gestures_ =
       pointer_event_manager_->PrimaryPointerdownCanceled(
-          targeted_event.Event().unique_touch_event_id);
+          gesture_event.unique_touch_event_id);
+
+  if (!RuntimeEnabledFeatures::TouchTextEditingRedesignEnabled() ||
+      suppress_mouse_events_from_gestures_ ||
+      suppress_selection_on_repeated_tap_down_ ||
+      gesture_event.TapDownCount() <= 1) {
+    return WebInputEventResult::kNotHandled;
+  }
+
+  const WebMouseEvent fake_mouse_down(
+      WebInputEvent::Type::kMouseDown, gesture_event,
+      WebPointerProperties::Button::kLeft, gesture_event.TapDownCount(),
+      static_cast<WebInputEvent::Modifiers>(
+          gesture_event.GetModifiers() |
+          WebInputEvent::Modifiers::kLeftButtonDown |
+          WebInputEvent::Modifiers::kIsCompatibilityEventForTouch),
+      gesture_event.TimeStamp());
+  const HitTestResult& current_hit_test = targeted_event.GetHitTestResult();
+  const HitTestLocation& current_hit_test_location =
+      targeted_event.GetHitTestLocation();
+  selection_controller_->HandleMousePressEvent(MouseEventWithHitTestResults(
+      fake_mouse_down, current_hit_test_location, current_hit_test));
+
   return WebInputEventResult::kNotHandled;
 }
 
@@ -269,6 +293,7 @@
   // mean for for TEs?  What's the right balance here? crbug.com/617255
   WebInputEventResult mouse_down_event_result =
       WebInputEventResult::kHandledSuppressed;
+  suppress_selection_on_repeated_tap_down_ = true;
   if (!suppress_mouse_events_from_gestures_) {
     mouse_event_manager_->SetClickCount(gesture_event.TapCount());
 
@@ -284,6 +309,7 @@
               true));
     }
     if (mouse_down_event_result == WebInputEventResult::kNotHandled) {
+      suppress_selection_on_repeated_tap_down_ = false;
       mouse_down_event_result = mouse_event_manager_->HandleMousePressEvent(
           MouseEventWithHitTestResults(
               fake_mouse_down, current_hit_test_location, current_hit_test));
diff --git a/third_party/blink/renderer/core/input/gesture_manager.h b/third_party/blink/renderer/core/input/gesture_manager.h
index a10766b..c1cc6e0 100644
--- a/third_party/blink/renderer/core/input/gesture_manager.h
+++ b/third_party/blink/renderer/core/input/gesture_manager.h
@@ -120,6 +120,11 @@
   // firing for the current gesture sequence (i.e. until next GestureTapDown).
   bool suppress_mouse_events_from_gestures_;
 
+  // Set on GestureTap if the default mouse down behaviour was suppressed. When
+  // this happens, we also suppress the default selection behaviour of the
+  // subsequent GestureTapDown if it occurs in the same gesture sequence.
+  bool suppress_selection_on_repeated_tap_down_ = true;
+
   bool gesture_context_menu_deferred_;
 
   gfx::PointF long_press_position_in_root_frame_;
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index c0babe7..290b915 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -603,34 +603,17 @@
       AutosizeHorizontalScrollbarMode() != mojom::blink::ScrollbarMode::kAuto) {
     h_mode = AutosizeHorizontalScrollbarMode();
     v_mode = AutosizeVerticalScrollbarMode();
-
-    if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff &&
-        v_mode == mojom::blink::ScrollbarMode::kAlwaysOff) {
-      TRACE_EVENT_INSTANT1(
-          TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-          "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-          ScrollbarDisableReason::kAutosizeMode);
-    }
     return;
   }
 
   LocalFrame* frame = GetFrame();
   if (!frame) {
-    // GetFrame() returns null if either Document::dom_window_ or
-    // DOMWindow::frame_ is null.
-    TRACE_EVENT_INSTANT1(
-        TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-        "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-        !GetDocument().domWindow() ? ScrollbarDisableReason::kNullDomWindow
-                                   : ScrollbarDisableReason::kNullFrame);
-
     RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
   }
 
   // ClipsContent() is false means that the client wants to paint the whole
   // contents of the frame without scrollbars, which is for printing etc.
-  ScrollbarDisableReason reason;
-  if (!frame->ClipsContent(&reason)) {
+  if (!frame->ClipsContent()) {
     bool disable_scrollbars = true;
 #if BUILDFLAG(IS_ANDROID)
     // However, Android WebView has a setting recordFullDocument. When it's set
@@ -645,10 +628,6 @@
     }
 #endif
     if (disable_scrollbars) {
-      TRACE_EVENT_INSTANT1(
-          TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-          "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-          reason);
       RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
     }
   }
@@ -656,11 +635,6 @@
   if (FrameOwner* owner = frame->Owner()) {
     // Setting scrolling="no" on an iframe element disables scrolling.
     if (owner->ScrollbarMode() == mojom::blink::ScrollbarMode::kAlwaysOff) {
-      TRACE_EVENT_INSTANT1(
-          TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-          "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-          ScrollbarDisableReason::kIframeScrollingNo);
-
       RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
     }
   }
@@ -669,11 +643,6 @@
   if (Node* body = document.body()) {
     // Framesets can't scroll.
     if (body->GetLayoutObject() && body->GetLayoutObject()->IsFrameSet()) {
-      TRACE_EVENT_INSTANT1(
-          TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-          "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-          ScrollbarDisableReason::kFrameSet);
-
       RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
     }
   }
@@ -681,11 +650,6 @@
   if (LocalFrameView* frameView = GetFrameView()) {
     // Scrollbars can be disabled by LocalFrameView::setCanHaveScrollbars.
     if (!frameView->CanHaveScrollbars()) {
-      TRACE_EVENT_INSTANT1(
-          TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-          "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-          ScrollbarDisableReason::kFrameViewCanHaveScrollbarsFalse);
-
       RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
     }
   }
@@ -711,11 +675,6 @@
     // Overflow is always hidden when stand-alone SVG documents are embedded.
     if (To<LayoutSVGRoot>(viewport)
             ->IsEmbeddedThroughFrameContainingSVGDocument()) {
-      TRACE_EVENT_INSTANT1(
-          TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-          "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-          ScrollbarDisableReason::kSVGRoot);
-
       RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
     }
   }
@@ -743,14 +702,6 @@
   if (overflow_y == EOverflow::kScroll)
     v_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
 
-  if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff &&
-      v_mode == mojom::blink::ScrollbarMode::kAlwaysOff) {
-    TRACE_EVENT_INSTANT1(
-        TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-        "CalculateScrollbarModes", TRACE_EVENT_SCOPE_THREAD, "disable_reason",
-        ScrollbarDisableReason::kOverflowHidden);
-  }
-
 #undef RETURN_SCROLLBAR_MODE
 }
 
diff --git a/third_party/blink/renderer/core/layout/scroll_anchor.cc b/third_party/blink/renderer/core/layout/scroll_anchor.cc
index 73be5bb..c2d2de3 100644
--- a/third_party/blink/renderer/core/layout/scroll_anchor.cc
+++ b/third_party/blink/renderer/core/layout/scroll_anchor.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 
+#include "base/trace_event/typed_macros.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
@@ -333,7 +334,7 @@
 }
 
 void ScrollAnchor::FindAnchor() {
-  TRACE_EVENT0("blink", "ScrollAnchor::findAnchor");
+  TRACE_EVENT0("blink", "ScrollAnchor::FindAnchor");
 
   bool found_priority_anchor = FindAnchorInPriorityCandidates();
   if (!found_priority_anchor)
@@ -343,6 +344,11 @@
     anchor_object_->SetIsScrollAnchorObject();
     saved_relative_offset_ =
         ComputeRelativeOffset(anchor_object_, scroller_, corner_);
+    TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"), "FindAnchor",
+                        "anchor_object_", anchor_object_->DebugName());
+    TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"), "FindAnchor",
+                        "saved_relative_offset_",
+                        saved_relative_offset_.ToString());
     anchor_is_cv_auto_without_layout_ =
         DisplayLockUtilities::IsAutoWithoutLayout(*anchor_object_);
   }
@@ -596,6 +602,11 @@
                         ToRoundedVector2d(saved_relative_offset_);
 
   LayoutRect anchor_rect = RelativeBounds(anchor_object_, scroller_);
+  TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+                      "ComputeAdjustment", "anchor_object_",
+                      anchor_object_->DebugName());
+  TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+                      "ComputeAdjustment", "delta", delta.ToString());
 
   // Only adjust on the block layout axis.
   const LayoutBox* scroller_box = ScrollerLayoutBox(scroller_);
@@ -637,6 +648,8 @@
 }
 
 void ScrollAnchor::Adjust() {
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+               "ScrollAnchor::Adjust");
   if (!queued_)
     return;
   queued_ = false;
@@ -644,6 +657,8 @@
   if (!anchor_object_)
     return;
   gfx::Vector2d adjustment = ComputeAdjustment();
+  TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"), "Adjust",
+                      "adjustment", adjustment.ToString());
 
   // We should pick a new anchor if we had an unlaid-out content-visibility
   // auto. It should have been laid out, so if it is still the best candidate,
@@ -661,15 +676,21 @@
     return;
   }
 
-  scroller_->SetScrollOffset(
-      scroller_->GetScrollOffset() + ScrollOffset(adjustment),
-      mojom::blink::ScrollType::kAnchoring);
+  ScrollOffset new_offset =
+      scroller_->GetScrollOffset() + ScrollOffset(adjustment);
+
+  TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"), "Adjust",
+                      "new_offset", new_offset.ToString());
+
+  scroller_->SetScrollOffset(new_offset, mojom::blink::ScrollType::kAnchoring);
 
   UseCounter::Count(ScrollerLayoutBox(scroller_)->GetDocument(),
                     WebFeature::kScrollAnchored);
 }
 
 bool ScrollAnchor::RestoreAnchor(const SerializedAnchor& serialized_anchor) {
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+               "ScrollAnchor::RestoreAnchor");
   if (!scroller_ || !serialized_anchor.IsValid()) {
     return false;
   }
@@ -704,6 +725,9 @@
     return false;
   }
 
+  TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"), "RestoreAnchor",
+                      "found_elements_length", found_elements->length());
+
   for (unsigned index = 0; index < found_elements->length(); index++) {
     Element* anchor_element = found_elements->item(index);
     LayoutObject* anchor_object = anchor_element->GetLayoutObject();
@@ -732,6 +756,9 @@
         ScrollOffset(serialized_anchor.relative_offset.X().ToFloat(),
                      serialized_anchor.relative_offset.Y().ToFloat());
     desired_offset -= delta;
+    TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+                        "RestoreAnchor", "anchor_object",
+                        anchor_object->DebugName());
     scroller_->SetScrollOffset(desired_offset,
                                mojom::blink::ScrollType::kAnchoring);
     FindAnchor();
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
index 1b9726e..f9fe753 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h"
 
+#include "base/trace_event/typed_macros.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
@@ -98,6 +99,7 @@
 }
 
 bool ElementFragmentAnchor::Invoke() {
+  TRACE_EVENT("blink", "ElementFragmentAnchor::Invoke");
   if (!frame_ || !anchor_node_)
     return false;
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 796d2c3..a9d602d 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -997,7 +997,7 @@
 
   bool needs_horizontal_scrollbar;
   bool needs_vertical_scrollbar;
-  ComputeScrollbarExistence(kLayout, needs_horizontal_scrollbar,
+  ComputeScrollbarExistence(needs_horizontal_scrollbar,
                             needs_vertical_scrollbar);
 
   if (!is_horizontal_scrollbar_frozen && !is_vertical_scrollbar_frozen &&
@@ -1149,7 +1149,7 @@
       GetLayoutBox()->GetFrame()->GetSettings()->GetViewportEnabled()) {
     bool needs_horizontal_scrollbar;
     bool needs_vertical_scrollbar;
-    ComputeScrollbarExistence(kRootScrollerChange, needs_horizontal_scrollbar,
+    ComputeScrollbarExistence(needs_horizontal_scrollbar,
                               needs_vertical_scrollbar);
     SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
     SetHasVerticalScrollbar(needs_vertical_scrollbar);
@@ -1269,7 +1269,7 @@
 
   bool needs_horizontal_scrollbar;
   bool needs_vertical_scrollbar;
-  ComputeScrollbarExistence(kStyleChange, needs_horizontal_scrollbar,
+  ComputeScrollbarExistence(needs_horizontal_scrollbar,
                             needs_vertical_scrollbar, kOverflowIndependent);
 
   // Avoid some unnecessary computation if there were and will be no scrollbars.
@@ -1304,7 +1304,7 @@
 
   bool needs_horizontal_scrollbar;
   bool needs_vertical_scrollbar;
-  ComputeScrollbarExistence(kOverflowRecalc, needs_horizontal_scrollbar,
+  ComputeScrollbarExistence(needs_horizontal_scrollbar,
                             needs_vertical_scrollbar);
 
   bool horizontal_scrollbar_should_change =
@@ -1524,7 +1524,6 @@
 }
 
 void PaintLayerScrollableArea::ComputeScrollbarExistence(
-    ComputeScrollbarExistenceReason reason,
     bool& needs_horizontal_scrollbar,
     bool& needs_vertical_scrollbar,
     ComputeScrollbarExistenceOption option) const {
@@ -1537,10 +1536,6 @@
       GetLayoutBox()->StyleRef().ScrollbarWidth() == EScrollbarWidth::kNone) {
     needs_horizontal_scrollbar = false;
     needs_vertical_scrollbar = false;
-    TraceComputeScrollbarExistence(
-        reason, needs_horizontal_scrollbar, needs_vertical_scrollbar, option,
-        true /* early_exit */, mojom::blink::ScrollbarMode::kAuto,
-        mojom::blink::ScrollbarMode::kAuto);
     return;
   }
 
@@ -1609,9 +1604,6 @@
   // If this is being performed before layout, we want to only update scrollbar
   // existence if its based on purely style based reasons.
   if (option == kOverflowIndependent) {
-    TraceComputeScrollbarExistence(reason, needs_horizontal_scrollbar,
-                                   needs_vertical_scrollbar, option,
-                                   false /* early_exit */, h_mode, v_mode);
     return;
   }
 
@@ -1630,9 +1622,6 @@
                                  VisibleContentRect(kIncludeScrollbars).width();
     }
   }
-  TraceComputeScrollbarExistence(reason, needs_horizontal_scrollbar,
-                                 needs_vertical_scrollbar, option,
-                                 false /* early_exit */, h_mode, v_mode);
 }
 
 bool PaintLayerScrollableArea::TryRemovingAutoScrollbars(
@@ -3092,30 +3081,4 @@
       ->OwnerNodeId();
 }
 
-void PaintLayerScrollableArea::TraceComputeScrollbarExistence(
-    ComputeScrollbarExistenceReason reason,
-    bool needs_horizontal_scrollbar,
-    bool needs_vertical_scrollbar,
-    ComputeScrollbarExistenceOption option,
-    bool early_exit,
-    mojom::blink::ScrollbarMode h_mode,
-    mojom::blink::ScrollbarMode v_mode) const {
-  TRACE_EVENT_INSTANT(
-      TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.scrollbars"),
-      "ComputeScrollbarExistence", [&](perfetto::EventContext ctx) {
-        ctx.AddDebugAnnotation("reason", reason);
-        ctx.AddDebugAnnotation("needs_horizontal", needs_horizontal_scrollbar);
-        ctx.AddDebugAnnotation("needs_vertical", needs_vertical_scrollbar);
-        ctx.AddDebugAnnotation("option", option);
-        ctx.AddDebugAnnotation("early_exit", early_exit);
-        ctx.AddDebugAnnotation("h_mode", static_cast<int>(h_mode));
-        ctx.AddDebugAnnotation("v_mode", static_cast<int>(v_mode));
-        ctx.AddDebugAnnotation("layer_size", Size().ToString());
-        ctx.AddDebugAnnotation("overflow_rect", overflow_rect_.ToString());
-        ctx.AddDebugAnnotation("is_root", Layer()->IsRootLayer());
-        ctx.AddDebugAnnotation("is_main_frame",
-                               GetLayoutBox()->GetFrame()->IsMainFrame());
-      });
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 11a3250..acba828 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -47,7 +47,6 @@
 #include "base/check_op.h"
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/scroll_anchor.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
@@ -656,26 +655,11 @@
     kDependsOnOverflow,
     kOverflowIndependent
   };
-  enum ComputeScrollbarExistenceReason {
-    kLayout,
-    kStyleChange,
-    kOverflowRecalc,
-    kRootScrollerChange,
-  };
   void ComputeScrollbarExistence(
-      ComputeScrollbarExistenceReason,
       bool& needs_horizontal_scrollbar,
       bool& needs_vertical_scrollbar,
       ComputeScrollbarExistenceOption = kDependsOnOverflow) const;
 
-  void TraceComputeScrollbarExistence(ComputeScrollbarExistenceReason reason,
-                                      bool needs_horizontal_scrollbar,
-                                      bool needs_vertical_scrollbar,
-                                      ComputeScrollbarExistenceOption option,
-                                      bool early_exit,
-                                      mojom::blink::ScrollbarMode h_mode,
-                                      mojom::blink::ScrollbarMode v_mode) const;
-
   // If the content fits entirely in the area without auto scrollbars, returns
   // true to try to remove them. This is a heuristic and can be incorrect if the
   // content size depends on the scrollbar size (e.g., percentage sizing).
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.cc b/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.cc
index ea9dde3..5b75523 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.cc
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.cc
@@ -245,8 +245,14 @@
     if (!src_origin_) {
       allowlist.self_if_matches = self_origin_->ToUrlOrigin();
     } else if (!src_origin_->IsOpaque()) {
-      allowlist.allowed_origins.emplace_back(
-          OriginWithPossibleWildcards::FromOrigin(src_origin_->ToUrlOrigin()));
+      absl::optional<OriginWithPossibleWildcards>
+          maybe_origin_with_possible_wildcards =
+              OriginWithPossibleWildcards::FromOrigin(
+                  src_origin_->ToUrlOrigin());
+      if (maybe_origin_with_possible_wildcards.has_value()) {
+        allowlist.allowed_origins.emplace_back(
+            *maybe_origin_with_possible_wildcards);
+      }
     } else {
       allowlist.matches_opaque_src = true;
     }
@@ -286,9 +292,16 @@
       // when parsing an iframe allow attribute.
       else if (src_origin_ && EqualIgnoringASCIICase(origin_string, "'src'")) {
         if (!src_origin_->IsOpaque()) {
-          origin_with_possible_wildcards =
-              OriginWithPossibleWildcards::FromOrigin(
-                  src_origin_->ToUrlOrigin());
+          absl::optional<OriginWithPossibleWildcards>
+              maybe_origin_with_possible_wildcards =
+                  OriginWithPossibleWildcards::FromOrigin(
+                      src_origin_->ToUrlOrigin());
+          if (maybe_origin_with_possible_wildcards.has_value()) {
+            origin_with_possible_wildcards =
+                *maybe_origin_with_possible_wildcards;
+          } else {
+            continue;
+          }
         } else {
           target_is_opaque = true;
         }
@@ -304,7 +317,7 @@
         absl::optional<OriginWithPossibleWildcards>
             maybe_origin_with_possible_wildcards =
                 OriginWithPossibleWildcards::Parse(origin_string.Utf8(), type);
-        if (maybe_origin_with_possible_wildcards) {
+        if (maybe_origin_with_possible_wildcards.has_value()) {
           origin_with_possible_wildcards =
               *maybe_origin_with_possible_wildcards;
         } else {
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc b/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc
index 6cb8b161..dba2be3 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc
@@ -1046,14 +1046,14 @@
   ParsedPermissionsPolicy test_policy = {
       {mojom::blink::PermissionsPolicyFeature::kFullscreen,
        /*allowed_origins=*/
-       {blink::OriginWithPossibleWildcards::FromOrigin(url_origin_a_),
-        blink::OriginWithPossibleWildcards::FromOrigin(url_origin_b_)},
+       {*blink::OriginWithPossibleWildcards::FromOrigin(url_origin_a_),
+        *blink::OriginWithPossibleWildcards::FromOrigin(url_origin_b_)},
        /*self_if_matches=*/absl::nullopt,
        /*matches_all_origins=*/false,
        /*matches_opaque_src=*/false},
       {mojom::blink::PermissionsPolicyFeature::kGeolocation,
        /*=allowed_origins*/
-       {blink::OriginWithPossibleWildcards::FromOrigin(url_origin_a_)},
+       {*blink::OriginWithPossibleWildcards::FromOrigin(url_origin_a_)},
        /*self_if_matches=*/absl::nullopt,
        /*matches_all_origins=*/false,
        /*matches_opaque_src=*/false}};
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index e7ea902..9748ea6 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -321,12 +321,17 @@
     return;
   }
 
-  TRACE_EVENT2("blink", "ScrollableArea::SetScrollOffset", "cur_x",
-               GetScrollOffset().x(), "cur_y", GetScrollOffset().y());
-  TRACE_EVENT_INSTANT1("blink", "Type", TRACE_EVENT_SCOPE_THREAD, "type",
+  TRACE_EVENT("blink", "ScrollableArea::SetScrollOffset", "offset",
+              offset.ToString());
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+                       "SetScrollOffset", TRACE_EVENT_SCOPE_THREAD,
+                       "current_offset", GetScrollOffset().ToString());
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+                       "SetScrollOffset", TRACE_EVENT_SCOPE_THREAD, "type",
                        scroll_type);
-  TRACE_EVENT_INSTANT1("blink", "Behavior", TRACE_EVENT_SCOPE_THREAD,
-                       "behavior", behavior);
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("blink.debug"),
+                       "SetScrollOffset", TRACE_EVENT_SCOPE_THREAD, "behavior",
+                       behavior);
 
   if (behavior == mojom::blink::ScrollBehavior::kAuto)
     behavior = ScrollBehaviorStyle();
diff --git a/third_party/blink/renderer/core/testing/data/core_test_bundle_data.filelist b/third_party/blink/renderer/core/testing/data/core_test_bundle_data.filelist
index ea5c08b..d0b62135 100644
--- a/third_party/blink/renderer/core/testing/data/core_test_bundle_data.filelist
+++ b/third_party/blink/renderer/core/testing/data/core_test_bundle_data.filelist
@@ -79,6 +79,7 @@
 testing/data/display_none_frame.html
 testing/data/div_with_image.html
 testing/data/domfocusout_domfocusin_events.html
+testing/data/double_press_selection.html
 testing/data/dv-size.html
 testing/data/editable_elements.html
 testing/data/email-form.html
diff --git a/third_party/blink/renderer/core/testing/data/double_press_selection.html b/third_party/blink/renderer/core/testing/data/double_press_selection.html
new file mode 100644
index 0000000..9978951
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/data/double_press_selection.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<div id='targetdiv' style='font-size:500%;width:50px;'>
+<p id='target' contenteditable>
+Test selection
+</p>
+<p id="onselectstartfalse" onselectstart="return false;" contenteditable>
+Test selection
+</p>
+</div>
+</body>
+</html>
diff --git a/third_party/blink/renderer/modules/smart_card/BUILD.gn b/third_party/blink/renderer/modules/smart_card/BUILD.gn
index 552d64c..34871b2 100644
--- a/third_party/blink/renderer/modules/smart_card/BUILD.gn
+++ b/third_party/blink/renderer/modules/smart_card/BUILD.gn
@@ -8,6 +8,8 @@
   sources = [
     "smart_card_connection.cc",
     "smart_card_connection.h",
+    "smart_card_context.cc",
+    "smart_card_context.h",
     "smart_card_error.cc",
     "smart_card_error.h",
     "smart_card_reader.cc",
@@ -18,5 +20,7 @@
     "smart_card_reader_presence_observer.h",
     "smart_card_resource_manager.cc",
     "smart_card_resource_manager.h",
+    "smart_card_util.cc",
+    "smart_card_util.h",
   ]
 }
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_context.cc b/third_party/blink/renderer/modules/smart_card/smart_card_context.cc
new file mode 100644
index 0000000..416d199
--- /dev/null
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_context.cc
@@ -0,0 +1,367 @@
+// 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/modules/smart_card/smart_card_context.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_flags.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_in.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state_out.h"
+#include "third_party/blink/renderer/modules/smart_card/smart_card_connection.h"
+#include "third_party/blink/renderer/modules/smart_card/smart_card_error.h"
+#include "third_party/blink/renderer/modules/smart_card/smart_card_util.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+
+namespace blink {
+namespace {
+constexpr char kContextUnavailable[] = "Context unavailable.";
+
+device::mojom::blink::SmartCardReaderStateFlagsPtr ToMojomStateFlags(
+    const SmartCardReaderStateFlags& flags) {
+  auto mojom_flags = device::mojom::blink::SmartCardReaderStateFlags::New();
+  mojom_flags->unaware = flags.unaware();
+  mojom_flags->ignore = flags.ignore();
+  mojom_flags->changed = flags.changed();
+  mojom_flags->unknown = flags.unknown();
+  mojom_flags->unavailable = flags.unavailable();
+  mojom_flags->empty = flags.empty();
+  mojom_flags->present = flags.present();
+  mojom_flags->exclusive = flags.exclusive();
+  mojom_flags->inuse = flags.inuse();
+  mojom_flags->mute = flags.mute();
+  mojom_flags->unpowered = flags.unpowered();
+  return mojom_flags;
+}
+
+Vector<device::mojom::blink::SmartCardReaderStateInPtr> ToMojomReaderStatesIn(
+    const HeapVector<Member<SmartCardReaderStateIn>>& reader_states) {
+  Vector<device::mojom::blink::SmartCardReaderStateInPtr> mojom_reader_states;
+  mojom_reader_states.reserve(reader_states.size());
+
+  for (const Member<SmartCardReaderStateIn>& state_in : reader_states) {
+    mojom_reader_states.push_back(
+        device::mojom::blink::SmartCardReaderStateIn::New(
+            state_in->readerName(),
+            ToMojomStateFlags(*state_in->currentState())));
+  }
+
+  return mojom_reader_states;
+}
+
+SmartCardReaderStateFlags* ToV8ReaderStateFlags(
+    const device::mojom::blink::SmartCardReaderStateFlags& mojom_state_flags) {
+  auto* state_flags = SmartCardReaderStateFlags::Create();
+  state_flags->setUnaware(mojom_state_flags.unaware);
+  state_flags->setIgnore(mojom_state_flags.ignore);
+  state_flags->setChanged(mojom_state_flags.changed);
+  state_flags->setUnknown(mojom_state_flags.unknown);
+  state_flags->setUnavailable(mojom_state_flags.unavailable);
+  state_flags->setEmpty(mojom_state_flags.empty);
+  state_flags->setPresent(mojom_state_flags.present);
+  state_flags->setExclusive(mojom_state_flags.exclusive);
+  state_flags->setInuse(mojom_state_flags.inuse);
+  state_flags->setMute(mojom_state_flags.mute);
+  state_flags->setUnpowered(mojom_state_flags.unpowered);
+  return state_flags;
+}
+
+HeapVector<Member<SmartCardReaderStateOut>> ToV8ReaderStatesOut(
+    Vector<device::mojom::blink::SmartCardReaderStateOutPtr>&
+        mojom_reader_states) {
+  HeapVector<Member<SmartCardReaderStateOut>> reader_states;
+  reader_states.reserve(mojom_reader_states.size());
+
+  for (auto& mojom_state_out : mojom_reader_states) {
+    auto* state_out = SmartCardReaderStateOut::Create();
+    state_out->setReaderName(mojom_state_out->reader);
+    state_out->setEventState(
+        ToV8ReaderStateFlags(*mojom_state_out->event_state));
+    state_out->setAnswerToReset(
+        DOMArrayBuffer::Create(mojom_state_out->answer_to_reset.data(),
+                               mojom_state_out->answer_to_reset.size()));
+    reader_states.push_back(state_out);
+  }
+
+  return reader_states;
+}
+
+void RejectWithAbortionReason(ScriptPromiseResolver* resolver,
+                              AbortSignal* signal) {
+  CHECK(signal->aborted());
+
+  ScriptState* script_state = resolver->GetScriptState();
+  if (!IsInParallelAlgorithmRunnable(resolver->GetExecutionContext(),
+                                     script_state)) {
+    return;
+  }
+
+  ScriptState::Scope script_state_scope(script_state);
+  resolver->Reject(signal->reason(script_state));
+}
+
+}  // anonymous namespace
+
+class SmartCardContext::GetStatusChangeAbortAlgorithm final
+    : public AbortSignal::Algorithm {
+ public:
+  explicit GetStatusChangeAbortAlgorithm(SmartCardContext* blink_scard_context)
+      : blink_scard_context_(blink_scard_context) {}
+  ~GetStatusChangeAbortAlgorithm() override = default;
+
+  void Run() override { blink_scard_context_->AbortGetStatusChange(); }
+
+  void Trace(Visitor* visitor) const override {
+    visitor->Trace(blink_scard_context_);
+    Algorithm::Trace(visitor);
+  }
+
+ private:
+  Member<SmartCardContext> blink_scard_context_;
+};
+
+SmartCardContext::SmartCardContext(
+    mojo::PendingRemote<device::mojom::blink::SmartCardContext> pending_context,
+    ExecutionContext* execution_context)
+    : ExecutionContextClient(execution_context),
+      scard_context_(execution_context) {
+  scard_context_.Bind(
+      std::move(pending_context),
+      execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI));
+  scard_context_.set_disconnect_handler(WTF::BindOnce(
+      &SmartCardContext::CloseMojoConnection, WrapWeakPersistent(this)));
+}
+
+ScriptPromise SmartCardContext::listReaders(ScriptState* script_state,
+                                            ExceptionState& exception_state) {
+  if (!EnsureMojoConnection(exception_state) ||
+      !EnsureNoOperationInProgress(exception_state)) {
+    return ScriptPromise();
+  }
+
+  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+      script_state, exception_state.GetContext());
+
+  list_readers_request_ = resolver;
+  scard_context_->ListReaders(
+      WTF::BindOnce(&SmartCardContext::OnListReadersDone, WrapPersistent(this),
+                    WrapPersistent(resolver)));
+
+  return resolver->Promise();
+}
+
+ScriptPromise SmartCardContext::getStatusChange(
+    ScriptState* script_state,
+    const HeapVector<Member<SmartCardReaderStateIn>>& reader_states,
+    AbortSignal* signal,
+    ExceptionState& exception_state) {
+  if (!EnsureMojoConnection(exception_state) ||
+      !EnsureNoOperationInProgress(exception_state)) {
+    return ScriptPromise();
+  }
+
+  if (signal->aborted()) {
+    return ScriptPromise::Reject(
+        script_state, get_status_change_abort_signal_->reason(script_state));
+  }
+  CHECK(!get_status_change_abort_signal_);
+  CHECK(!get_status_change_abort_handle_);
+  get_status_change_abort_signal_ = signal;
+  get_status_change_abort_handle_ =
+      get_status_change_abort_signal_->AddAlgorithm(
+          MakeGarbageCollected<GetStatusChangeAbortAlgorithm>(this));
+
+  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+      script_state, exception_state.GetContext());
+
+  get_status_change_request_ = resolver;
+  scard_context_->GetStatusChange(
+      base::TimeDelta::Max(), ToMojomReaderStatesIn(reader_states),
+      WTF::BindOnce(&SmartCardContext::OnGetStatusChangeDone,
+                    WrapPersistent(this), WrapPersistent(resolver)));
+
+  return resolver->Promise();
+}
+
+ScriptPromise SmartCardContext::connect(
+    ScriptState* script_state,
+    const String& reader_name,
+    V8SmartCardAccessMode access_mode,
+    const Vector<V8SmartCardProtocol>& preferred_protocols,
+    ExceptionState& exception_state) {
+  if (!EnsureMojoConnection(exception_state) ||
+      !EnsureNoOperationInProgress(exception_state)) {
+    return ScriptPromise();
+  }
+
+  ScriptPromiseResolver* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+      script_state, exception_state.GetContext());
+
+  connect_request_ = resolver;
+  scard_context_->Connect(
+      reader_name, ToMojoSmartCardShareMode(access_mode),
+      ToMojoSmartCardProtocols(preferred_protocols),
+      WTF::BindOnce(&SmartCardContext::OnConnectDone, WrapPersistent(this),
+                    WrapPersistent(resolver)));
+
+  return resolver->Promise();
+}
+
+ScriptPromise SmartCardContext::connect(ScriptState* script_state,
+                                        const String& reader_name,
+                                        V8SmartCardAccessMode access_mode,
+                                        ExceptionState& exception_state) {
+  return connect(script_state, reader_name, access_mode,
+                 Vector<V8SmartCardProtocol>(), exception_state);
+}
+
+void SmartCardContext::Trace(Visitor* visitor) const {
+  visitor->Trace(scard_context_);
+  visitor->Trace(list_readers_request_);
+  visitor->Trace(connect_request_);
+  visitor->Trace(get_status_change_request_);
+  visitor->Trace(get_status_change_abort_signal_);
+  visitor->Trace(get_status_change_abort_handle_);
+  ScriptWrappable::Trace(visitor);
+  ExecutionContextClient::Trace(visitor);
+}
+
+void SmartCardContext::CloseMojoConnection() {
+  scard_context_.reset();
+
+  auto reject = [](ScriptPromiseResolver* resolver) {
+    if (!resolver) {
+      return;
+    }
+    ScriptState* script_state = resolver->GetScriptState();
+    if (!IsInParallelAlgorithmRunnable(resolver->GetExecutionContext(),
+                                       script_state)) {
+      return;
+    }
+    ScriptState::Scope script_state_scope(script_state);
+    resolver->RejectWithDOMException(DOMExceptionCode::kInvalidStateError,
+                                     kContextUnavailable);
+  };
+
+  reject(list_readers_request_.Release());
+  reject(connect_request_.Release());
+
+  ResetAbortSignal();
+  reject(get_status_change_request_.Release());
+}
+
+void SmartCardContext::ResetAbortSignal() {
+  if (get_status_change_abort_handle_) {
+    CHECK(get_status_change_abort_signal_);
+    get_status_change_abort_signal_->RemoveAlgorithm(
+        get_status_change_abort_handle_);
+    get_status_change_abort_handle_ = nullptr;
+  }
+  get_status_change_abort_signal_ = nullptr;
+}
+
+bool SmartCardContext::EnsureNoOperationInProgress(
+    ExceptionState& exception_state) const {
+  if (list_readers_request_ || connect_request_ || get_status_change_request_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "An operation is in progress.");
+    return false;
+  }
+  return true;
+}
+
+bool SmartCardContext::EnsureMojoConnection(
+    ExceptionState& exception_state) const {
+  if (!scard_context_.is_bound()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kContextUnavailable);
+    return false;
+  }
+  return true;
+}
+
+void SmartCardContext::OnListReadersDone(
+    ScriptPromiseResolver* resolver,
+    device::mojom::blink::SmartCardListReadersResultPtr result) {
+  CHECK_EQ(list_readers_request_, resolver);
+  list_readers_request_ = nullptr;
+
+  if (result->is_error()) {
+    auto* error = SmartCardError::Create(result->get_error());
+    resolver->Reject(error);
+    return;
+  }
+
+  resolver->Resolve(std::move(result->get_readers()));
+}
+
+void SmartCardContext::OnGetStatusChangeDone(
+    ScriptPromiseResolver* resolver,
+    device::mojom::blink::SmartCardStatusChangeResultPtr result) {
+  CHECK(get_status_change_abort_signal_);
+  CHECK_EQ(get_status_change_request_, resolver);
+  get_status_change_request_ = nullptr;
+
+  if (result->is_error()) {
+    if (get_status_change_abort_signal_->aborted() &&
+        result->get_error() ==
+            device::mojom::blink::SmartCardError::kCancelled) {
+      CHECK(!get_status_change_abort_handle_);
+      RejectWithAbortionReason(resolver, get_status_change_abort_signal_);
+    } else {
+      resolver->Reject(SmartCardError::Create(result->get_error()));
+    }
+    ResetAbortSignal();
+    return;
+  }
+
+  ResetAbortSignal();
+
+  resolver->Resolve(ToV8ReaderStatesOut(result->get_reader_states()));
+}
+
+void SmartCardContext::OnCancelDone(
+    device::mojom::blink::SmartCardResultPtr result) {
+  if (result->is_error()) {
+    LOG(WARNING) << "Cancel operation failed: " << result->get_error();
+  }
+}
+
+void SmartCardContext::OnConnectDone(
+    ScriptPromiseResolver* resolver,
+    device::mojom::blink::SmartCardConnectResultPtr result) {
+  CHECK_EQ(connect_request_, resolver);
+  connect_request_ = nullptr;
+
+  if (result->is_error()) {
+    auto* error = SmartCardError::Create(result->get_error());
+    resolver->Reject(error);
+    return;
+  }
+
+  device::mojom::blink::SmartCardConnectSuccessPtr& success =
+      result->get_success();
+
+  auto* connection = MakeGarbageCollected<SmartCardConnection>(
+      std::move(success->connection), success->active_protocol,
+      GetExecutionContext());
+
+  resolver->Resolve(connection);
+}
+
+void SmartCardContext::AbortGetStatusChange() {
+  CHECK(get_status_change_abort_signal_);
+  CHECK(get_status_change_abort_handle_);
+  // Aborting shouldn't be possible if there's no ongoing getStatusChange()
+  // request in the first place.
+  CHECK(get_status_change_request_);
+
+  // You can only abort once.
+  get_status_change_abort_signal_->RemoveAlgorithm(
+      get_status_change_abort_handle_);
+  get_status_change_abort_handle_ = nullptr;
+
+  scard_context_->Cancel(
+      WTF::BindOnce(&SmartCardContext::OnCancelDone, WrapPersistent(this)));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_context.h b/third_party/blink/renderer/modules/smart_card/smart_card_context.h
new file mode 100644
index 0000000..ee14a9d9
--- /dev/null
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_context.h
@@ -0,0 +1,83 @@
+// 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_MODULES_SMART_CARD_SMART_CARD_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SMART_CARD_SMART_CARD_CONTEXT_H_
+
+#include "services/device/public/mojom/smart_card.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_access_mode.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_protocol.h"
+#include "third_party/blink/renderer/core/dom/abort_signal.h"
+#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+
+namespace blink {
+
+class ScriptPromiseResolver;
+class SmartCardReaderStateIn;
+
+class SmartCardContext final : public ScriptWrappable,
+                               public ExecutionContextClient {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  SmartCardContext(mojo::PendingRemote<device::mojom::blink::SmartCardContext>,
+                   ExecutionContext*);
+
+  // SmartCardContext idl
+  ScriptPromise listReaders(ScriptState* script_state,
+                            ExceptionState& exception_state);
+
+  ScriptPromise getStatusChange(
+      ScriptState* script_state,
+      const HeapVector<Member<SmartCardReaderStateIn>>& reader_states,
+      AbortSignal* signal,
+      ExceptionState& exception_state);
+
+  ScriptPromise connect(ScriptState* script_state,
+                        const String& reader_name,
+                        V8SmartCardAccessMode access_mode,
+                        const Vector<V8SmartCardProtocol>& preferred_protocols,
+                        ExceptionState& exception_state);
+
+  ScriptPromise connect(ScriptState* script_state,
+                        const String& reader_name,
+                        V8SmartCardAccessMode access_mode,
+                        ExceptionState& exception_state);
+
+  // ScriptWrappable overrides
+  void Trace(Visitor*) const override;
+
+ private:
+  class GetStatusChangeAbortAlgorithm;
+
+  void CloseMojoConnection();
+  bool EnsureNoOperationInProgress(ExceptionState& exception_state) const;
+  bool EnsureMojoConnection(ExceptionState& exception_state) const;
+  void OnListReadersDone(ScriptPromiseResolver* resolver,
+                         device::mojom::blink::SmartCardListReadersResultPtr);
+  void OnGetStatusChangeDone(
+      ScriptPromiseResolver* resolver,
+      device::mojom::blink::SmartCardStatusChangeResultPtr result);
+  void OnCancelDone(device::mojom::blink::SmartCardResultPtr result);
+  void OnConnectDone(ScriptPromiseResolver* resolver,
+                     device::mojom::blink::SmartCardConnectResultPtr result);
+  void AbortGetStatusChange();
+  void ResetAbortSignal();
+
+  HeapMojoRemote<device::mojom::blink::SmartCardContext> scard_context_;
+  Member<ScriptPromiseResolver> list_readers_request_;
+  Member<ScriptPromiseResolver> connect_request_;
+
+  Member<AbortSignal> get_status_change_abort_signal_;
+  Member<AbortSignal::AlgorithmHandle> get_status_change_abort_handle_;
+  Member<ScriptPromiseResolver> get_status_change_request_;
+};
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SMART_CARD_SMART_CARD_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_context.idl b/third_party/blink/renderer/modules/smart_card/smart_card_context.idl
new file mode 100644
index 0000000..0c7d7c5
--- /dev/null
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_context.idl
@@ -0,0 +1,56 @@
+// 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.
+
+// https://github.com/WICG/web-smart-card/blob/main/README.md#smartcardcontext
+
+dictionary SmartCardReaderStateIn {
+  required DOMString readerName;
+  required SmartCardReaderStateFlags currentState;
+};
+
+dictionary SmartCardReaderStateOut {
+  required DOMString readerName;
+  required SmartCardReaderStateFlags eventState;
+  required ArrayBuffer answerToReset;
+};
+
+dictionary SmartCardReaderStateFlags {
+  boolean unaware = false;
+  boolean ignore = false;
+  boolean changed = false;
+  boolean unknown = false;
+  boolean unavailable = false;
+  boolean empty = false;
+  boolean present = false;
+  boolean exclusive = false;
+  boolean inuse = false;
+  boolean mute = false;
+  boolean unpowered = false;
+};
+
+dictionary SmartCardConnectResult {
+  required SmartCardConnection connection;
+  required SmartCardProtocol activeProtocol;
+};
+
+[
+  Exposed=Window,
+  RuntimeEnabled=SmartCard,
+  SecureContext,
+  IsolatedContext
+] interface SmartCardContext {
+  [CallWith=ScriptState, RaisesException]
+  Promise<sequence<DOMString>> listReaders();
+
+  [CallWith=ScriptState, RaisesException]
+  Promise<sequence<SmartCardReaderStateOut>> getStatusChange(
+      sequence<SmartCardReaderStateIn> readerStates,
+      AbortSignal signal);
+
+  [CallWith=ScriptState, RaisesException]
+  Promise<SmartCardConnectResult> connect(
+      DOMString readerName,
+      SmartCardAccessMode accessMode,
+      optional sequence<SmartCardProtocol> preferredProtocols);
+};
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_reader.cc b/third_party/blink/renderer/modules/smart_card/smart_card_reader.cc
index ba0b4f9..fa2d6f2 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_reader.cc
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_reader.cc
@@ -5,12 +5,15 @@
 #include "third_party/blink/renderer/modules/smart_card/smart_card_reader.h"
 
 #include "services/device/public/mojom/smart_card.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_access_mode.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_protocol.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/smart_card/smart_card_connection.h"
 #include "third_party/blink/renderer/modules/smart_card/smart_card_error.h"
 #include "third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h"
+#include "third_party/blink/renderer/modules/smart_card/smart_card_util.h"
 
 namespace blink {
 namespace {
@@ -34,38 +37,6 @@
   }
 }
 
-device::mojom::blink::SmartCardProtocolsPtr ToMojoProtocols(
-    const Vector<V8SmartCardProtocol>& preferred_protocols) {
-  auto result = device::mojom::blink::SmartCardProtocols::New();
-
-  for (const auto& protocol : preferred_protocols) {
-    switch (protocol.AsEnum()) {
-      case blink::V8SmartCardProtocol::Enum::kRaw:
-        result->raw = true;
-        break;
-      case blink::V8SmartCardProtocol::Enum::kT0:
-        result->t0 = true;
-        break;
-      case blink::V8SmartCardProtocol::Enum::kT1:
-        result->t1 = true;
-        break;
-    }
-  }
-
-  return result;
-}
-
-device::mojom::blink::SmartCardShareMode ToMojoShareMode(
-    V8SmartCardAccessMode access_mode) {
-  switch (access_mode.AsEnum()) {
-    case blink::V8SmartCardAccessMode::Enum::kShared:
-      return device::mojom::blink::SmartCardShareMode::kShared;
-    case blink::V8SmartCardAccessMode::Enum::kExclusive:
-      return device::mojom::blink::SmartCardShareMode::kExclusive;
-    case blink::V8SmartCardAccessMode::Enum::kDirect:
-      return device::mojom::blink::SmartCardShareMode::kDirect;
-  }
-}
 }  // anonymous namespace
 
 SmartCardReader::SmartCardReader(SmartCardResourceManager* resource_manager,
@@ -97,7 +68,8 @@
   connect_promises_.insert(resolver);
 
   resource_manager_->Connect(
-      name_, ToMojoShareMode(access_mode), ToMojoProtocols(preferred_protocols),
+      name_, ToMojoSmartCardShareMode(access_mode),
+      ToMojoSmartCardProtocols(preferred_protocols),
       WTF::BindOnce(&SmartCardReader::OnConnectDone, WrapPersistent(this),
                     WrapPersistent(resolver)));
   return resolver->Promise();
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_reader.h b/third_party/blink/renderer/modules/smart_card/smart_card_reader.h
index 50a04bad..90ee2ce 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_reader.h
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_reader.h
@@ -9,8 +9,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_access_mode.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_protocol.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_reader_state.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -18,6 +16,8 @@
 
 namespace blink {
 class SmartCardResourceManager;
+class V8SmartCardAccessMode;
+class V8SmartCardProtocol;
 
 class MODULES_EXPORT SmartCardReader
     : public EventTargetWithInlineData,
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc
index 1d78cf1..d231eb6 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/mojom/smart_card/smart_card.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/navigator_base.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
+#include "third_party/blink/renderer/modules/smart_card/smart_card_context.h"
 #include "third_party/blink/renderer/modules/smart_card/smart_card_error.h"
 #include "third_party/blink/renderer/modules/smart_card/smart_card_reader_presence_event.h"
 #include "third_party/blink/renderer/modules/smart_card/smart_card_reader_presence_observer.h"
@@ -127,6 +128,7 @@
   visitor->Trace(receiver_);
   visitor->Trace(get_readers_promises_);
   visitor->Trace(watch_for_readers_promises_);
+  visitor->Trace(create_context_promises_);
   visitor->Trace(reader_cache_);
   visitor->Trace(presence_observer_);
   ScriptWrappable::Trace(visitor);
@@ -179,6 +181,25 @@
   return promise;
 }
 
+ScriptPromise SmartCardResourceManager::establishContext(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
+  if (ShouldBlockSmartCardServiceCall(GetExecutionContext(), exception_state)) {
+    return ScriptPromise();
+  }
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+      script_state, exception_state.GetContext());
+  create_context_promises_.insert(resolver);
+
+  EnsureServiceConnection();
+
+  service_->CreateContext(
+      WTF::BindOnce(&SmartCardResourceManager::OnCreateContextDone,
+                    WrapPersistent(this), WrapPersistent(resolver)));
+  return resolver->Promise();
+}
+
 void SmartCardResourceManager::FinishGetReaders(
     ScriptPromiseResolver* resolver,
     mojom::blink::SmartCardGetReadersResultPtr result) {
@@ -271,6 +292,24 @@
   watch_for_readers_promises_.clear();
 }
 
+void SmartCardResourceManager::OnCreateContextDone(
+    ScriptPromiseResolver* resolver,
+    device::mojom::blink::SmartCardCreateContextResultPtr result) {
+  DCHECK(create_context_promises_.Contains(resolver));
+  create_context_promises_.erase(resolver);
+
+  if (result->is_error()) {
+    auto* error = SmartCardError::Create(result->get_error());
+    resolver->Reject(error);
+    return;
+  }
+
+  auto* context = MakeGarbageCollected<SmartCardContext>(
+      std::move(result->get_context()), GetExecutionContext());
+
+  resolver->Resolve(context);
+}
+
 SmartCardReaderPresenceObserver*
 SmartCardResourceManager::GetOrCreatePresenceObserver() {
   if (!presence_observer_) {
@@ -315,16 +354,22 @@
   }
 
   // Similar protection is unnecessary when rejecting a promise.
-  for (auto& resolver : watch_for_readers_promises_) {
-    ScriptState* script_state = resolver->GetScriptState();
-    if (!IsInParallelAlgorithmRunnable(resolver->GetExecutionContext(),
-                                       script_state)) {
-      continue;
-    }
-    ScriptState::Scope script_state_scope(script_state);
-    resolver->RejectWithDOMException(DOMExceptionCode::kInvalidStateError,
-                                     kServiceDisconnected);
-  }
+  auto rejectPromises =
+      [](HeapHashSet<Member<ScriptPromiseResolver>>& resolvers) {
+        for (auto& resolver : resolvers) {
+          ScriptState* script_state = resolver->GetScriptState();
+          if (!IsInParallelAlgorithmRunnable(resolver->GetExecutionContext(),
+                                             script_state)) {
+            continue;
+          }
+          ScriptState::Scope script_state_scope(script_state);
+          resolver->RejectWithDOMException(DOMExceptionCode::kInvalidStateError,
+                                           kServiceDisconnected);
+        }
+        resolvers.clear();
+      };
+  rejectPromises(watch_for_readers_promises_);
+  rejectPromises(create_context_promises_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h
index 34caffa..fbfcb582 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h
@@ -52,6 +52,8 @@
                            ExceptionState& exception_state);
   ScriptPromise watchForReaders(ScriptState* script_state,
                                 ExceptionState& exception_state);
+  ScriptPromise establishContext(ScriptState* script_state,
+                                 ExceptionState& exception_state);
 
   // mojom::blink::SmartCardServiceClient overrides:
   void ReaderAdded(SmartCardReaderInfoPtr reader_info) override;
@@ -75,6 +77,9 @@
   void UpdateReadersCache(mojom::blink::SmartCardGetReadersResultPtr);
 
   void OnServiceClientRegistered(bool supports_reader_presence_observer);
+  void OnCreateContextDone(
+      ScriptPromiseResolver*,
+      device::mojom::blink::SmartCardCreateContextResultPtr);
   void ResolveWatchForReadersPromise(ScriptPromiseResolver* resolver);
   SmartCardReaderPresenceObserver* GetOrCreatePresenceObserver();
 
@@ -84,6 +89,7 @@
       receiver_;
   HeapHashSet<Member<ScriptPromiseResolver>> get_readers_promises_;
   HeapHashSet<Member<ScriptPromiseResolver>> watch_for_readers_promises_;
+  HeapHashSet<Member<ScriptPromiseResolver>> create_context_promises_;
   bool tracking_started_ = false;
 
   // maps a reader name to its object
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.idl b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.idl
index 784801c1..dfac8be0 100644
--- a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.idl
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.idl
@@ -16,4 +16,7 @@
 
   [CallWith=ScriptState, RaisesException]
   Promise<SmartCardReaderPresenceObserver> watchForReaders();
+
+  [CallWith=ScriptState, RaisesException]
+  Promise<SmartCardContext> establishContext();
 };
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_util.cc b/third_party/blink/renderer/modules/smart_card/smart_card_util.cc
new file mode 100644
index 0000000..770b175
--- /dev/null
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_util.cc
@@ -0,0 +1,45 @@
+// 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/modules/smart_card/smart_card_util.h"
+#include "services/device/public/mojom/smart_card.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_access_mode.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_smart_card_protocol.h"
+
+namespace blink {
+
+device::mojom::blink::SmartCardShareMode ToMojoSmartCardShareMode(
+    V8SmartCardAccessMode access_mode) {
+  switch (access_mode.AsEnum()) {
+    case blink::V8SmartCardAccessMode::Enum::kShared:
+      return device::mojom::blink::SmartCardShareMode::kShared;
+    case blink::V8SmartCardAccessMode::Enum::kExclusive:
+      return device::mojom::blink::SmartCardShareMode::kExclusive;
+    case blink::V8SmartCardAccessMode::Enum::kDirect:
+      return device::mojom::blink::SmartCardShareMode::kDirect;
+  }
+}
+
+device::mojom::blink::SmartCardProtocolsPtr ToMojoSmartCardProtocols(
+    const Vector<V8SmartCardProtocol>& preferred_protocols) {
+  auto result = device::mojom::blink::SmartCardProtocols::New();
+
+  for (const auto& protocol : preferred_protocols) {
+    switch (protocol.AsEnum()) {
+      case blink::V8SmartCardProtocol::Enum::kRaw:
+        result->raw = true;
+        break;
+      case blink::V8SmartCardProtocol::Enum::kT0:
+        result->t0 = true;
+        break;
+      case blink::V8SmartCardProtocol::Enum::kT1:
+        result->t1 = true;
+        break;
+    }
+  }
+
+  return result;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_util.h b/third_party/blink/renderer/modules/smart_card/smart_card_util.h
new file mode 100644
index 0000000..04eca3ec
--- /dev/null
+++ b/third_party/blink/renderer/modules/smart_card/smart_card_util.h
@@ -0,0 +1,24 @@
+// 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_MODULES_SMART_CARD_SMART_CARD_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SMART_CARD_SMART_CARD_UTIL_H_
+
+#include "services/device/public/mojom/smart_card.mojom-blink-forward.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class V8SmartCardAccessMode;
+class V8SmartCardProtocol;
+
+device::mojom::blink::SmartCardShareMode ToMojoSmartCardShareMode(
+    V8SmartCardAccessMode access_mode);
+
+device::mojom::blink::SmartCardProtocolsPtr ToMojoSmartCardProtocols(
+    const Vector<V8SmartCardProtocol>& preferred_protocols);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SMART_CARD_SMART_CARD_UTIL_H_
diff --git a/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.cc b/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.cc
index 8c73bd9..5ebe6678 100644
--- a/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.cc
+++ b/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.cc
@@ -10,7 +10,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "media/base/audio_bus.h"
-#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/sample_rates.h"
@@ -60,7 +59,6 @@
     const media::AudioGlitchInfo& glitch_info) {
   TRACE_EVENT2("audio", "WebRtcAudioDeviceImpl::RenderData", "sample_rate",
                sample_rate, "audio_delay_ms", audio_delay.InMilliseconds());
-  media::CheckGlitchInfoAndDelay(glitch_info, audio_delay);
   {
     base::AutoLock auto_lock(lock_);
     cumulative_glitch_info_ += glitch_info;
diff --git a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
index a27b684..ad31dd7 100644
--- a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
+++ b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
@@ -601,7 +601,6 @@
                                 media::AudioBus* audio_bus) {
   DCHECK(sink_->CurrentThreadIsRenderingThread());
   DCHECK_LE(sink_params_.channels(), 8);
-  media::CheckGlitchInfoAndDelay(glitch_info, delay);
   base::AutoLock auto_lock(lock_);
   if (!source_)
     return 0;
diff --git a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
index 62ece0d..b4291bf 100644
--- a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
+++ b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
@@ -43,6 +43,25 @@
   }
 
   void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
+                         const viz::SharedImageFormat& si_format,
+                         const gfx::ColorSpace& color_space,
+                         GrSurfaceOrigin surface_origin,
+                         SkAlphaType alpha_type,
+                         uint32_t usage,
+                         gpu::Mailbox& mailbox,
+                         gpu::SyncToken& sync_token) override {
+    auto* sii = SharedImageInterface();
+    if (!sii) {
+      return;
+    }
+    mailbox = sii->CreateSharedImage(
+        si_format, gpu_memory_buffer->GetSize(), color_space, surface_origin,
+        alpha_type, usage, "WebGraphicsContext3DVideoFramePool",
+        gpu_memory_buffer->CloneHandle());
+    sync_token = sii->GenVerifiedSyncToken();
+  }
+
+  void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
                          gfx::BufferPlane plane,
                          const gfx::ColorSpace& color_space,
                          GrSurfaceOrigin surface_origin,
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
index 926867e..0478d265 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
@@ -428,13 +428,13 @@
     policy->SetHeaderPolicy(
         {{{mojom::blink::PermissionsPolicyFeature::
                kBrowsingTopics, /*allowed_origins=*/
-           {blink::OriginWithPossibleWildcards::FromOrigin(origin_b)},
+           {*blink::OriginWithPossibleWildcards::FromOrigin(origin_b)},
            /*self_if_matches=*/absl::nullopt,
            /*matches_all_origins=*/false,
            /*matches_opaque_src=*/false},
           {mojom::blink::PermissionsPolicyFeature::
                kSharedStorage, /*allowed_origins=*/
-           {blink::OriginWithPossibleWildcards::FromOrigin(origin_b)},
+           {*blink::OriginWithPossibleWildcards::FromOrigin(origin_b)},
            /*self_if_matches=*/absl::nullopt,
            /*matches_all_origins=*/false,
            /*matches_opaque_src=*/false}}});
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
index d829e01..b56e2b8f 100644
--- a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
+++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
@@ -68,6 +68,25 @@
   }
 
   void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
+                         const viz::SharedImageFormat& si_format,
+                         const gfx::ColorSpace& color_space,
+                         GrSurfaceOrigin surface_origin,
+                         SkAlphaType alpha_type,
+                         uint32_t usage,
+                         gpu::Mailbox& mailbox,
+                         gpu::SyncToken& sync_token) override {
+    auto* sii = SharedImageInterface();
+    if (!sii) {
+      return;
+    }
+    mailbox = sii->CreateSharedImage(si_format, gpu_memory_buffer->GetSize(),
+                                     color_space, surface_origin, alpha_type,
+                                     usage, "WebRTCVideoFramePool",
+                                     gpu_memory_buffer->CloneHandle());
+    sync_token = sii->GenVerifiedSyncToken();
+  }
+
+  void CreateSharedImage(gfx::GpuMemoryBuffer* gpu_memory_buffer,
                          gfx::BufferPlane plane,
                          const gfx::ColorSpace& color_space,
                          GrSurfaceOrigin surface_origin,
diff --git a/third_party/blink/web_tests/FlagExpectations/highdpi b/third_party/blink/web_tests/FlagExpectations/highdpi
index bee1e2f..dfa4ef3 100644
--- a/third_party/blink/web_tests/FlagExpectations/highdpi
+++ b/third_party/blink/web_tests/FlagExpectations/highdpi
@@ -65,8 +65,6 @@
 # ref tests that fail in highdpi
 crbug.com/1179572 animations/cross-fade-webkit-mask-box-image.html [ Failure ]
 
-crbug.com/1353771 virtual/view-transition/external/wpt/css/css-view-transitions/new-content-with-overflow-zoomed.html [ Failure ]
-crbug.com/1353771 virtual/view-transition/external/wpt/css/css-view-transitions/old-content-with-overflow-zoomed.html [ Failure ]
 crbug.com/1353771 virtual/view-transition/external/wpt/css/css-view-transitions/element-with-overflow.html [ Failure ]
 crbug.com/1412910 virtual/view-transition/external/wpt/css/css-view-transitions/fractional-translation-from-position.html [ Failure ]
 crbug.com/1414855 virtual/view-transition/external/wpt/css/css-view-transitions/new-content-captures-spans.html [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index f1e405cba..1e2e815 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -389,9 +389,6 @@
 crbug.com/1024331 external/wpt/css/css-text/line-breaking/line-breaking-019.html [ Skip ]
 
 # CSS Text features that are not planned to implement.
-external/wpt/css/css-text/parsing/word-boundary-detection-computed.html [ Skip ]
-external/wpt/css/css-text/parsing/word-boundary-detection-invalid.html [ Skip ]
-external/wpt/css/css-text/parsing/word-boundary-detection-valid.html [ Skip ]
 external/wpt/css/css-text/parsing/word-boundary-expansion-computed.html [ Skip ]
 external/wpt/css/css-text/parsing/word-boundary-expansion-invalid.html [ Skip ]
 external/wpt/css/css-text/parsing/word-boundary-expansion-valid.html [ Skip ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 363005ad..f9abe2f1 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -48,7 +48,6 @@
 crbug.com/24182 [ Debug Linux ] http/tests/cache/subresource-expiration-1.html [ Slow ]
 crbug.com/24182 [ Release ] http/tests/cache/subresource-expiration-1.html [ Slow ]
 crbug.com/24182 [ Debug Linux ] http/tests/cache/subresource-expiration-2.html [ Slow ]
-crbug.com/24182 [ Debug Mac12 ] http/tests/cache/subresource-expiration-2.html [ Slow ]
 crbug.com/24182 [ Release ] http/tests/cache/subresource-expiration-2.html [ Slow ]
 crbug.com/24182 [ Release ] http/tests/misc/object-embedding-svg-delayed-size-negotiation-2.htm [ Slow ]
 crbug.com/24182 http/tests/misc/uncacheable-script-repeated.html [ Slow ]
@@ -89,7 +88,6 @@
 crbug.com/24182 [ Mac12 ] jquery/traversing.html [ Slow ]
 crbug.com/24182 [ Release Win ] jquery/traversing.html [ Slow ]
 crbug.com/24182 [ Debug Linux ] media/controls/controls-cast-do-not-fade-out.html [ Slow ]
-crbug.com/24182 [ Debug Mac12 ] media/controls/controls-cast-do-not-fade-out.html [ Slow ]
 crbug.com/24182 [ Release ] media/controls/controls-cast-do-not-fade-out.html [ Slow ]
 crbug.com/24182 [ Release ] media/controls/controls-cast-overlay-slow-fade.html [ Slow ]
 crbug.com/24182 svg/filters/big-sized-filter.svg [ Slow ]
@@ -124,9 +122,7 @@
 crbug.com/24182 [ Mac12 ] fast/events/tabindex-focus-blur-all.html [ Slow ]
 crbug.com/24182 [ Release Win ] fast/events/tabindex-focus-blur-all.html [ Slow ]
 crbug.com/451577 [ Debug Linux ] fast/dom/gc-treescope.html [ Slow ]
-crbug.com/451577 [ Debug Mac12 ] fast/dom/gc-treescope.html [ Slow ]
 crbug.com/451577 [ Debug Linux ] fast/frames/calculate-round.html [ Slow ]
-crbug.com/451577 [ Debug Mac12 ] fast/frames/calculate-round.html [ Slow ]
 
 # Most DevTools tests are slow in Debug.
 webkit.org/b/90488 [ Debug ] http/tests/devtools/* [ Slow ]
@@ -186,7 +182,6 @@
 
 # These tests are intentionally SLOW because of throttled loading.
 crbug.com/73609 http/tests/media/video-play-stall.html [ Slow ]
-crbug.com/73609 [ Debug Mac12 ] http/tests/media/video-preload-metadata.html [ Slow ]
 crbug.com/73609 [ Release ] http/tests/media/video-preload-metadata.html [ Slow ]
 crbug.com/869829 http/tests/media/video-cancel-load.html [ Slow ]
 crbug.com/870259 http/tests/media/video-throttled-load-metadata.html [ Slow ]
@@ -210,7 +205,6 @@
 crbug.com/453312 [ Mac12 ] html5lib/generated/run-tests1-data.html [ Slow ]
 crbug.com/453312 [ Release Win ] html5lib/generated/run-tests1-data.html [ Slow ]
 crbug.com/453312 [ Debug Linux ] html5lib/generated/run-tests2-data.html [ Slow ]
-crbug.com/453312 [ Debug Mac12 ] html5lib/generated/run-tests2-data.html [ Slow ]
 crbug.com/453312 [ Debug Mac13 ] html5lib/generated/run-tests2-data.html [ Slow ]
 crbug.com/453312 [ Linux Release ] html5lib/generated/run-tests2-data.html [ Slow ]
 crbug.com/453312 [ Mac10.15 Release ] html5lib/generated/run-tests2-data.html [ Slow ]
@@ -255,11 +249,9 @@
 crbug.com/522646 http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Slow ]
 crbug.com/411164 [ Win ] http/tests/security/powerfulFeatureRestrictions/serviceworker-on-insecure-origin.html [ Slow ]
 crbug.com/357427 [ Debug Linux ] http/tests/workers/terminate-during-sync-operation-file.html [ Slow ]
-crbug.com/357427 [ Debug Mac12 ] http/tests/workers/terminate-during-sync-operation-file.html [ Slow ]
 crbug.com/357427 [ Release ] http/tests/workers/terminate-during-sync-operation-file.html [ Slow ]
 crbug.com/357427 http/tests/workers/terminate-during-sync-operation-filesystem.html [ Slow ]
 crbug.com/402379 [ Debug Linux ] storage/indexeddb/cursor-continue-validity.html [ Slow ]
-crbug.com/402379 [ Debug Mac12 ] storage/indexeddb/cursor-continue-validity.html [ Slow ]
 crbug.com/402379 [ Linux ] storage/indexeddb/mozilla/indexes.html [ Slow ]
 crbug.com/402379 [ Mac10.15 Release ] storage/indexeddb/mozilla/indexes.html [ Slow ]
 crbug.com/402379 [ Mac11 Release ] storage/indexeddb/mozilla/indexes.html [ Slow ]
@@ -269,7 +261,6 @@
 crbug.com/504703 inspector-protocol/debugger/debugger-step-into-dedicated-worker.js [ Slow ]
 
 crbug.com/331186 [ Debug Linux ] css3/flexbox/position-absolute-child.html [ Slow ]
-crbug.com/331186 [ Debug Mac12 ] css3/flexbox/position-absolute-child.html [ Slow ]
 
 crbug.com/372424 [ Linux ] fast/dom/DOMImplementation/createDocument-with-used-doctype.html [ Slow ]
 crbug.com/372424 [ Mac10.15 Release ] fast/dom/DOMImplementation/createDocument-with-used-doctype.html [ Slow ]
@@ -343,11 +334,9 @@
 crbug.com/1081534 [ Mac10.15 Release ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-transformclip.html [ Slow ]
 crbug.com/1081534 [ Mac11 Release ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-transformclip.html [ Slow ]
 crbug.com/1081534 [ Mac12 ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-transformclip.html [ Slow ]
-crbug.com/1081534 [ Debug Mac12 ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow.html [ Slow ]
 crbug.com/1081534 [ Mac10.14 Release ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow.html [ Slow ]
 crbug.com/1081534 [ Debug Mac13 ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-video.html [ Slow ]
 crbug.com/1081534 [ Mac10.14 Release ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-video.html [ Slow ]
-crbug.com/1081534 [ Debug Mac12 ] virtual/oopr-canvas2d/fast/canvas/canvas-blending-gradient-over-image.html [ Slow ]
 crbug.com/1081534 [ Mac ] virtual/oopr-canvas2d/fast/canvas/canvas-blending-gradient-over-pattern.html [ Slow ]
 crbug.com/1081534 [ Mac ] virtual/oopr-canvas2d/fast/canvas/canvas-blending-image-over-gradient.html [ Slow ]
 crbug.com/1081534 [ Mac ] virtual/oopr-canvas2d/fast/canvas/canvas-blending-pattern-over-color.html [ Slow ]
@@ -397,7 +386,6 @@
 
 # These font-display tests are slow as they test web fonts behavior around the 3-seconds timeout.
 crbug.com/564109 [ Debug Linux ] http/tests/webfont/font-display-intervention.html [ Slow ]
-crbug.com/564109 [ Debug Mac12 ] http/tests/webfont/font-display-intervention.html [ Slow ]
 crbug.com/564109 [ Release ] http/tests/webfont/font-display-intervention.html [ Slow ]
 
 # This test does a lot of IPC because it tests limits on IPC allocations,
@@ -410,7 +398,6 @@
 
 
 crbug.com/942951 [ Debug Linux ] media/controls/controls-layout-in-different-size.html [ Slow ]
-crbug.com/942951 [ Debug Mac12 ] media/controls/controls-layout-in-different-size.html [ Slow ]
 crbug.com/942951 [ Release ] media/controls/controls-layout-in-different-size.html [ Slow ]
 
 crbug.com/910627 webexposed/global-interface-listing-shared-worker.html [ Slow ]
@@ -426,7 +413,6 @@
 crbug.com/866850 [ Linux ] virtual/gpu/fast/canvas/canvas-blending-color-over-image.html [ Slow ]
 crbug.com/866850 [ Linux ] virtual/gpu/fast/canvas/canvas-blending-color-over-pattern.html [ Slow ]
 crbug.com/866850 [ Linux ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-image.html [ Slow ]
-crbug.com/866850 [ Debug Mac12 ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-image.html [ Slow ]
 crbug.com/866850 [ Debug Mac13 ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-image.html [ Slow ]
 crbug.com/866850 [ Linux ] virtual/gpu/fast/canvas/canvas-blending-image-over-gradient.html [ Slow ]
 crbug.com/866850 [ Mac12 Release ] virtual/gpu/fast/canvas/canvas-blending-image-over-gradient.html [ Slow ]
@@ -449,10 +435,8 @@
 crbug.com/866850 [ Mac ] virtual/gpu/fast/canvas/OffscreenCanvas-filter-in-worker.html [ Slow ]
 
 ### Some timeout tests in xmlhttprequest/timeout/ are slow
-crbug.com/869800 [ Debug Mac12 ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-aborted.html [ Slow ]
 crbug.com/869800 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-aborted.html [ Slow ]
 crbug.com/869800 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-simple.html [ Slow ]
-crbug.com/869800 [ Debug Mac12 ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-synconworker.html [ Slow ]
 crbug.com/869800 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-synconworker.html [ Slow ]
 
 ### Slow only on Mac10.13
@@ -624,11 +608,9 @@
 crbug.com/874695 http/tests/cookies/same-site/framed.https.html [ Slow ]
 crbug.com/874695 http/tests/credentialmanagement/credentialscontainer-create-with-virtual-authenticator.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/credentialmanagement/credentialscontainer-get-origins.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/credentialmanagement/credentialscontainer-get-origins.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/credentialmanagement/credentialscontainer-get-origins.html [ Slow ]
 crbug.com/874695 http/tests/credentialmanagement/register-then-sign.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/fetch/serviceworker/body-mixin-base-https-other-https.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/fetch/serviceworker/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/serviceworker/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/serviceworker/body-mixin.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/fetch/serviceworker/stream-reader-base-https-other-https.html [ Slow ]
@@ -637,17 +619,13 @@
 crbug.com/874695 http/tests/fetch/serviceworker/thorough/* [ Slow ]
 crbug.com/874695 http/tests/fetch/serviceworker-proxied/thorough/* [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/fetch/window/body-mixin-base-https-other-https.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/fetch/window/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/window/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/window/body-mixin.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/fetch/window/stream-reader-base-https-other-https.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/fetch/window/stream-reader-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/window/stream-reader-base-https-other-https.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/fetch/window/stream-reader.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/window/stream-reader.html [ Slow ]
 crbug.com/874695 http/tests/fetch/window/thorough/* [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/fetch/workers/body-mixin-base-https-other-https.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/fetch/workers/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/workers/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/fetch/workers/body-mixin.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/fetch/workers/stream-reader-base-https-other-https.html [ Slow ]
@@ -658,13 +636,11 @@
 crbug.com/874695 [ Linux Release ] http/tests/images/webp-progressive-load.html [ Slow ]
 crbug.com/874695 [ Mac Release ] http/tests/images/webp-progressive-load.html [ Slow ]
 crbug.com/874695 [ Release Win ] http/tests/images/webp-progressive-load.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/media/controls/toggle-class-with-state-source-buffer.html [ Slow ]
 crbug.com/874695 [ Linux ] http/tests/media/controls/toggle-class-with-state-source-buffer.html [ Slow ]
 crbug.com/874695 [ Mac Release ] http/tests/media/controls/toggle-class-with-state-source-buffer.html [ Slow ]
 crbug.com/874695 [ Release Win ] http/tests/media/controls/toggle-class-with-state-source-buffer.html [ Slow ]
 crbug.com/874695 http/tests/media/preload-conditions.html [ Slow ]
 crbug.com/874695 http/tests/media/video-buffered.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/media/video-play-stall-before-meta-data.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/media/video-play-stall-before-meta-data.html [ Slow ]
 crbug.com/874695 http/tests/misc/adopt-iframe-src-attr-after-remove.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/misc/async-script-removed.html [ Slow ]
@@ -685,7 +661,6 @@
 crbug.com/874695 http/tests/security/cross-frame-mouse-source-capabilities.html [ Slow ]
 crbug.com/874695 http/tests/security/frameNavigation/xss-ALLOWED-same-origin-top-navigation-without-user-gesture.html [ Slow ]
 crbug.com/874695 http/tests/security/video-poster-cross-origin-crash2.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/webfont/crbug-655076.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/webfont/crbug-655076.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/webfont/slow-loading.html [ Slow ]
 crbug.com/874695 http/tests/webmidi/midi-default-feature-policy.https.sub.html [ Slow ]
@@ -695,13 +670,11 @@
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-aborted.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-overridesexpires.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-simple.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-simple.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-simple.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-twice.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-overridesexpires.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-twice.html [ Slow ]
 crbug.com/874695 [ Release ] http/tests/xmlhttprequest/web-apps/013.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] images/image-page-injected-script-crash.html [ Slow ]
 crbug.com/874695 [ Release ] images/image-page-injected-script-crash.html [ Slow ]
 crbug.com/874695 images/yuv-decode-eligible/color-profile-layer-filter.html [ Slow ]
 crbug.com/874695 http/tests/inspector-protocol/accessibility/accessibility-nameSources-input-buttons.js [ Slow ]
@@ -710,17 +683,14 @@
 crbug.com/874695 media/autoplay-muted.html [ Slow ]
 crbug.com/874695 media/color-profile-video-seek-filter.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Slow ]
 crbug.com/874695 [ Release ] media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Slow ]
 crbug.com/874695 media/encrypted-media/encrypted-media-onencrypted.html [ Slow ]
 crbug.com/874695 [ Release ] media/encrypted-media/encrypted-media-setmediakeys-at-same-time.html [ Slow ]
 crbug.com/874695 [ Linux Release ] media/media-controls-tap-show-controls-without-activating.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] media/media-controls-tap-show-controls-without-activating.html [ Slow ]
 crbug.com/874695 [ Mac Release ] media/media-controls-tap-show-controls-without-activating.html [ Slow ]
 crbug.com/874695 [ Release Win ] media/media-controls-tap-show-controls-without-activating.html [ Slow ]
 crbug.com/874695 [ Release ] media/media-ended.html [ Slow ]
 crbug.com/874695 media/remoteplayback/prompt-twice-throws.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] media/track/track-cue-gc-wrapper.html [ Slow ]
 crbug.com/874695 [ Release ] media/track/track-cue-gc-wrapper.html [ Slow ]
 crbug.com/874695 media/unsupported-rtsp.html [ Slow ]
 crbug.com/874695 [ Release ] media/video-controls-always-visible-when-control-hovered.html [ Slow ]
@@ -729,7 +699,6 @@
 crbug.com/874695 media/video-controls-focus-movement-on-hide.html [ Slow ]
 crbug.com/874695 [ Release ] media/video-controls-hide-after-touch-on-control.html [ Slow ]
 crbug.com/874695 [ Release ] media/video-controls-hide-on-move-outside-controls.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] media/video-controls-show-on-focus.html [ Slow ]
 crbug.com/874695 [ Release ] media/video-controls-show-on-focus.html [ Slow ]
 crbug.com/874695 [ Release ] media/video-controls-visibility-multimodal-mouse-after-touch.html [ Slow ]
 crbug.com/874695 [ Release ] media/video-controls-visibility-multimodal-touch-after-mouse.html [ Slow ]
@@ -740,7 +709,6 @@
 crbug.com/874695 plugins/plugin-document-back-forward.html [ Slow ]
 crbug.com/874695 pointer-lock/locked-element-iframe-removed-from-dom.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] printing/webgl-oversized-printing.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] printing/webgl-oversized-printing.html [ Slow ]
 crbug.com/874695 [ Linux Release ] printing/webgl-oversized-printing.html [ Slow ]
 crbug.com/874695 [ Mac10.15 Release ] printing/webgl-oversized-printing.html [ Slow ]
 crbug.com/874695 [ Mac11 Release ] printing/webgl-oversized-printing.html [ Slow ]
@@ -784,7 +752,6 @@
 crbug.com/874695 virtual/gpu-rasterization/images/webp-color-profile-lossless.html [ Slow ]
 crbug.com/874695 [ Release ] virtual/stable/http/tests/navigation/new-window-redirect-history.html [ Slow ]
 crbug.com/874695 [ Debug Linux ] virtual/stable/media/stable/video-object-fit-stable.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] virtual/stable/media/stable/video-object-fit-stable.html [ Slow ]
 crbug.com/874695 [ Release ] virtual/stable/media/stable/video-object-fit-stable.html [ Slow ]
 crbug.com/874695 virtual/scalefactor200/css3/filters/effect-reference-zoom-hw.html [ Slow ]
 crbug.com/874695 virtual/threaded/fast/idle-callback/idle_periods.html [ Slow ]
@@ -798,12 +765,9 @@
 crbug.com/874695 virtual/threaded/http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js [ Slow ]
 crbug.com/874695 virtual/threaded/http/tests/devtools/tracing/timeline-script-parse.js [ Slow ]
 crbug.com/874695 [ Debug Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
 crbug.com/874695 [ Release ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow-desktop.html [ Slow ]
 crbug.com/874695 [ Release ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow-desktop.html [ Slow ]
 crbug.com/874695 [ Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Slow ]
-crbug.com/874695 [ Debug Mac12 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Slow ]
 crbug.com/874695 [ Mac Release ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Slow ]
 crbug.com/874695 [ Release Win ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Slow ]
 
@@ -815,7 +779,6 @@
 crbug.com/893015 [ Linux ] http/tests/fetch/chromium/response-json-gc-crash.html [ Slow ]
 crbug.com/893015 [ Linux ] http/tests/fetch/chromium/response-text-gc-crash.html [ Slow ]
 
-crbug.com/914981 [ Debug Mac12 ] media/controls/overflow-menu-pointer-selection.html [ Slow ]
 crbug.com/914981 [ Mac Release ] media/controls/overflow-menu-pointer-selection.html [ Slow ]
 crbug.com/914981 [ Mac Release ] fast/events/no-fake-mousemove.html [ Slow ]
 
@@ -913,7 +876,6 @@
 crbug.com/1091716 [ Mac12 ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-intrinsic-ratio.html [ Slow ]
 crbug.com/1091716 [ Release Win ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-intrinsic-ratio.html [ Slow ]
 crbug.com/1091716 [ Debug Linux ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-no-intrinsic-ratio.html [ Slow ]
-crbug.com/1091716 [ Debug Mac12 ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-no-intrinsic-ratio.html [ Slow ]
 crbug.com/1091716 [ Linux Release ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-no-intrinsic-ratio.html [ Slow ]
 crbug.com/1091716 [ Mac10.15 Release ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-no-intrinsic-ratio.html [ Slow ]
 crbug.com/1091716 [ Mac11 Release ] svg/as-object/sizing/svg-in-object-placeholder-percentage-auto-no-intrinsic-ratio.html [ Slow ]
@@ -945,7 +907,6 @@
 crbug.com/1233743 [ Mac12 ] svg/filters/feDisplacementMap.svg [ Slow ]
 crbug.com/1233743 [ Release Win ] svg/filters/feDisplacementMap.svg [ Slow ]
 
-crbug.com/1043669 [ Debug Mac12 ] inspector-protocol/emulation/set-vision-deficiency.js [ Slow ]
 crbug.com/1043669 [ Mac Release ] inspector-protocol/emulation/set-vision-deficiency.js [ Slow ]
 crbug.com/1092121 fast/css/large-list-of-rules-crash.html [ Slow ]
 crbug.com/1093849 external/wpt/dom/nodes/Element-classlist.html [ Slow ]
@@ -976,7 +937,6 @@
 crbug.com/1142023 [ Win10.20h2 ] http/tests/security/document-domain-canonicalizes-iframe.html [ Slow ]
 
 # crbug.com/1095379: These were all added here in an attempt to reduce flakiness.
-crbug.com/983788 [ Debug Mac12 ] http/tests/cookies/same-site/popup-cross-site.https.html [ Slow ]
 crbug.com/983788 [ Release ] http/tests/cookies/same-site/popup-cross-site.https.html [ Slow ]
 crbug.com/1049641 [ Debug ] http/tests/devtools/sources/debugger/debugger-disable-enable.js [ Slow ]
 crbug.com/1049641 [ Linux Release ] http/tests/devtools/sources/debugger/debugger-disable-enable.js [ Slow ]
@@ -985,11 +945,8 @@
 crbug.com/1049641 [ Mac12 Release ] http/tests/devtools/sources/debugger/debugger-disable-enable.js [ Slow ]
 crbug.com/1049641 [ Release Win ] http/tests/devtools/sources/debugger/debugger-disable-enable.js [ Slow ]
 crbug.com/947951 [ Win ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html [ Slow ]
-crbug.com/626703 [ Debug Mac12 ] external/wpt/preload/preload-with-type.html [ Slow ]
 crbug.com/626703 [ Mac Release ] external/wpt/preload/preload-with-type.html [ Slow ]
-crbug.com/626703 [ Debug Mac12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html [ Slow ]
 crbug.com/626703 [ Mac Release ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html [ Slow ]
-crbug.com/626703 [ Debug Mac12 ] external/wpt/infrastructure/testdriver/actions/pause.html [ Slow ]
 crbug.com/626703 [ Mac Release ] external/wpt/infrastructure/testdriver/actions/pause.html [ Slow ]
 crbug.com/626703 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html [ Slow ]
 crbug.com/982116 [ Linux ] http/tests/devtools/elements/styles-4/styles-new-API.js [ Slow ]
@@ -1000,7 +957,6 @@
 crbug.com/655458 external/wpt/workers/Worker_terminate_event_queue.htm [ Slow ]
 crbug.com/1073296 external/wpt/service-workers/service-worker/activation.https.html [ Slow ]
 crbug.com/678346 [ Debug Linux ] fast/dom/shadow/selections-in-shadow.html [ Slow ]
-crbug.com/678346 [ Debug Mac12 ] fast/dom/shadow/selections-in-shadow.html [ Slow ]
 crbug.com/678346 [ Debug Mac ] storage/indexeddb/index-cursor.html [ Slow ]
 crbug.com/799619 [ Debug ] http/tests/devtools/profiler/heap-snapshot-inspect-dom-wrapper.js [ Slow ]
 ###crbug.com/849979 media/video-layer-crash.html [ Slow ]
@@ -1054,7 +1010,6 @@
 crbug.com/978000 inspector-protocol/page/setWebLifecycleState.js [ Slow ]
 crbug.com/977015 [ Win ] http/tests/devtools/elements/shadow/shadow-slot-assignment.js [ Slow ]
 crbug.com/978304 http/tests/devtools/elements/styles-4/undo-add-property.js [ Slow ]
-crbug.com/981303 [ Debug Mac12 ] http/tests/media/controls/toggle-class-with-state-source.html [ Slow ]
 crbug.com/981303 [ Mac Release ] http/tests/media/controls/toggle-class-with-state-source.html [ Slow ]
 crbug.com/981331 fast/forms/form-submission-create-crash.xhtml [ Slow ]
 crbug.com/982290 [ Win ] http/tests/devtools/elements/styles-4/styles-do-not-detach-sourcemap-on-edits.js [ Slow ]
@@ -1065,7 +1020,6 @@
 crbug.com/999799 [ Mac12 ] external/wpt/html/rendering/dimension-attributes.html [ Slow ]
 crbug.com/999799 [ Linux ] external/wpt/html/rendering/dimension-attributes.html [ Slow ]
 crbug.com/990900 external/wpt/cookie-store/idlharness.tentative.https.any.serviceworker.html [ Slow ]
-crbug.com/1014327 [ Debug Mac12 ] external/wpt/preload/onerror-event.html [ Slow ]
 crbug.com/1014327 [ Mac Release ] external/wpt/preload/onerror-event.html [ Slow ]
 crbug.com/1017626 [ Linux ] external/wpt/compat/idlharness.window.html [ Slow ]
 crbug.com/1019662 external/wpt/html/dom/idlharness.worker.html [ Slow ]
@@ -1395,8 +1349,6 @@
 crbug.com/984467 fast/canvas/canvas-composite-stroke-alpha.html [ Slow ]
 crbug.com/1078863 [ Mac ] wpt_internal/speech/scripted/speechrecognition-restart-onend.html [ Slow ]
 crbug.com/1091948 [ Linux ] external/wpt/html/canvas/element/manual/fill-and-stroke-styles/canvas_colorsandstyles_createlineargradient_001.htm [ Slow ]
-crbug.com/1176802 [ Debug Mac12 ] external/wpt/feature-policy/feature-policy-frame-policy-timing.https.sub.html [ Slow ]
-crbug.com/1176802 [ Debug Mac12 ] external/wpt/permissions-policy/permissions-policy-frame-policy-timing.https.sub.html [ Slow ]
 crbug.com/1044825 http/tests/devtools/network/resource-priority.js [ Slow ]
 crbug.com/1046784 [ Debug ] http/tests/devtools/profiler/cpu-profiler-bottom-up-large-tree-search.js [ Slow ]
 crbug.com/1046784 [ Linux Release ] http/tests/devtools/profiler/cpu-profiler-bottom-up-large-tree-search.js [ Slow ]
@@ -1419,7 +1371,6 @@
 crbug.com/1186753 virtual/threaded-prefer-compositing/fast/scroll-snap/snaps-after-scrollbar-scrolling-thumb.html [ Slow ]
 crbug.com/1186753 virtual/threaded-prefer-compositing/fast/scroll-snap/snaps-after-scrollbar-scrolling-tap.html [ Slow ]
 crbug.com/1186753 [ Debug Linux ] virtual/threaded-prefer-compositing/fast/scroll-snap/animate-fling-to-snap-points-2.html [ Slow ]
-crbug.com/1186753 [ Debug Mac12 ] virtual/threaded-prefer-compositing/fast/scroll-snap/animate-fling-to-snap-points-2.html [ Slow ]
 crbug.com/1186753 [ Release ] virtual/threaded-prefer-compositing/fast/scroll-snap/animate-fling-to-snap-points-2.html [ Slow ]
 crbug.com/1361889 virtual/threaded-prefer-compositing/fast/scrolling/scrollbars/mouse-scrolling-on-div-custom-scrollbar.html [ Slow ]
 
@@ -1455,7 +1406,6 @@
 
 crbug.com/1301971 [ Debug Linux ] virtual/backface-visibility-interop/external/wpt/css/css-transforms/animation/transform-interpolation-verify-reftests.html [ Slow ]
 
-crbug.com/1300811 [ Debug Mac12 ] animations/interpolation/webkit-column-width-interpolation.html [ Slow ]
 crbug.com/1300811 [ Debug Linux ] animations/interpolation/webkit-column-width-interpolation.html [ Slow ]
 
 crbug.com/1228246 http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c2a93cd..1cd7b652 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1506,7 +1506,6 @@
 crbug.com/958381 [ Mac ] virtual/text-antialias/hyphen-min-preferred-width.html [ Failure ]
 crbug.com/1449411 [ Mac ] virtual/text-antialias/word-break-soft-hyphen.html [ Failure Pass ]
 crbug.com/958381 [ Win ] virtual/text-antialias/hyphen-min-preferred-width.html [ Failure ]
-crbug.com/958381 [ Debug Mac12 ] fragmentation/single-line-cells-paginated-with-text.html [ Failure ]
 
 # TablesNG ends
 
@@ -1606,12 +1605,9 @@
 crbug.com/1147859 [ Mac ] external/wpt/css/css-pseudo/highlight-painting-004.html [ Failure ]
 crbug.com/1172333 external/wpt/css/css-pseudo/first-letter-hi-001.html [ Failure ]
 crbug.com/1172333 external/wpt/css/css-pseudo/first-letter-digraph.html [ Failure ]
-crbug.com/1035708 [ Debug Mac12 ] wpt_internal/css/css-pseudo/spelling-error-color-003.html [ Failure ]
 crbug.com/1035708 wpt_internal/css/css-pseudo/spelling-error-color-dynamic-001.html [ Failure ]
 crbug.com/1035708 wpt_internal/css/css-pseudo/grammar-error-color-003.html [ Failure ]
 crbug.com/1035708 wpt_internal/css/css-pseudo/grammar-error-color-dynamic-001.html [ Failure ]
-crbug.com/1035708 [ Debug Mac12 ] wpt_internal/css/css-pseudo/grammar-error-color-dynamic-003.html [ Failure ]
-crbug.com/1035708 [ Debug Mac12 ] wpt_internal/css/css-pseudo/grammar-error-color-dynamic-004.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/target-text-004.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-004.html [ Failure ]
 crbug.com/1353352 fast/css3-text/css3-text-decoration/text-decoration-line-grammar-error.html [ Failure ]
@@ -1784,7 +1780,6 @@
 
 crbug.com/520188 [ Win ] http/tests/local/fileapi/file-last-modified-after-delete.html [ Failure Pass ]
 crbug.com/520611 [ Debug Linux ] fast/filesystem/workers/file-writer-events-shared-worker.html [ Failure Pass ]
-crbug.com/520611 [ Debug Mac12 ] fast/filesystem/workers/file-writer-events-shared-worker.html [ Failure Pass ]
 crbug.com/520194 http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-overridesexpires.html [ Failure Pass ]
 
 crbug.com/1051136 fast/forms/select/listbox-overlay-scrollbar.html [ Failure ]
@@ -2084,7 +2079,6 @@
 crbug.com/1051044 external/wpt/css/filter-effects/effect-reference-feimage-003.html [ Failure Pass ]
 
 crbug.com/524160 [ Debug Linux ] http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Timeout ]
-crbug.com/524160 [ Debug Mac12 ] http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html [ Timeout ]
 
 # based on Builder Win7 Tests (1) and flakyness dashboard
 crbug.com/1325545 [ Win ] external/wpt/cookie-store/cookieListItem_attributes.https.any.html [ Failure Pass ]
@@ -2945,7 +2939,6 @@
 crbug.com/626703 external/wpt/shape-detection/single-text-detection.https.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001.html [ Failure ]
 crbug.com/626703 [ Linux ] virtual/threaded/external/wpt/css/css-transforms/individual-transform/animation/individual-transform-ordering.html [ Failure ]
-crbug.com/626703 [ Mac12 ] virtual/threaded/external/wpt/css/css-transforms/individual-transform/animation/individual-transform-ordering.html [ Failure ]
 crbug.com/626703 virtual/fenced-frame-mparch/wpt_internal/fenced_frame/unfenced-top.https.html [ Skip Timeout ]
 crbug.com/626703 [ Linux ] virtual/fenced-frame-mparch/external/wpt/html/anonymous-iframe/web-lock.tentative.https.window.html [ Timeout ]
 crbug.com/626703 [ Linux ] virtual/isolate-sandboxed-iframes/external/wpt/html/infrastructure/urls/terminology-0/document-base-url-initiated-grand-parent.https.window.html [ Timeout ]
@@ -2987,7 +2980,6 @@
 crbug.com/1232504 [ Mac10.14 ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Crash Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/mediacapture-fromelement/capture.html [ Crash ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/mediacapture-streams/MediaDevices-enumerateDevices-per-origin-ids.sub.https.html [ Crash ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-simulcast.https.html [ Crash ]
 crbug.com/626703 external/wpt/css/css-fonts/font-size-adjust-013.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/white-space/pre-wrap-align-center-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/white-space/pre-wrap-align-center-002.html [ Failure ]
@@ -3050,11 +3042,7 @@
 crbug.com/626703 external/wpt/css/css-pseudo/svg-text-selection-002.html [ Failure ]
 crbug.com/626703 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/svg-text-selection-002.html [ Failure ]
 crbug.com/626703 virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/svg-text-selection-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-counter-styles/counter-style-at-rule/descriptor-prefix.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-counter-styles/counter-style-at-rule/system-additive.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-counter-styles/counter-style-at-rule/system-alphabetic.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-counter-styles/counter-style-at-rule/system-extends.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-counter-styles/counter-style-at-rule/system-symbolic.html [ Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/url/IdnaTestV2.window.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/css/css-box/margin-trim/block-container-non-adjoining-item.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-block-end-trimmed-only.html [ Failure ]
@@ -3095,7 +3083,6 @@
 crbug.com/626703 [ Win11 ] wpt_internal/geolocation-api/watchPosition-page-visibility.https.html [ Timeout ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/web-animations/idlharness.window.html [ Crash Failure ]
 crbug.com/626703 [ Mac ] external/wpt/url/a-element-xhtml.xhtml?exclude=(file|javascript|mailto) [ Crash Failure ]
-crbug.com/626703 [ Mac12-arm64 ] virtual/pending-beacon/external/wpt/pending-beacon/pending_post_beacon-cors.tentative.https.window.html [ Timeout ]
 crbug.com/626703 [ Win10.20h2 ] wpt_internal/geolocation-api/watchPosition-page-visibility.https.html [ Timeout ]
 crbug.com/626703 [ Win11 ] external/wpt/fetch/metadata/generated/element-meta-refresh.optional.sub.html [ Timeout ]
 crbug.com/626703 [ Win11 ] virtual/pending-beacon/external/wpt/pending-beacon/pending_post_beacon-cors.tentative.https.window.html [ Timeout ]
@@ -3106,7 +3093,6 @@
 crbug.com/626703 [ Linux ] external/wpt/shape-detection/detection-HTMLVideoElement.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/webxr/ar-module/xrSession_interactionMode.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/webxr/dom-overlay/ar_dom_overlay.https.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/webxr/dom-overlay/ar_dom_overlay_hit_test.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/webxr/hit-test/ar_hittest_source_cancel.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/webxr/hit-test/ar_hittest_subscription_inputSources.https.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/webxr/hit-test/ar_hittest_subscription_refSpaces.https.html [ Timeout ]
@@ -3516,8 +3502,6 @@
 crbug.com/1160916 external/wpt/css/css-backgrounds/border-radius-clip-001.html [ Failure Pass ]
 crbug.com/1160916 virtual/threaded/external/wpt/css/css-backgrounds/border-radius-clip-002.htm [ Failure Pass ]
 crbug.com/1160916 external/wpt/css/css-backgrounds/border-radius-clip-002.htm [ Failure Pass ]
-crbug.com/1160916 virtual/threaded/external/wpt/css/css-backgrounds/border-top-right-radius-010.xht [ Failure Pass ]
-crbug.com/1160916 external/wpt/css/css-backgrounds/border-top-right-radius-010.xht [ Failure Pass ]
 
 # [css-grid]
 crbug.com/1045599 external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-001.html [ Failure ]
@@ -3828,7 +3812,6 @@
 
 # Double tap on modern media controls is a bit more complicated on Mac but
 # since we are not targeting Mac yet we can come back and fix this later.
-crbug.com/783154 [ Debug Mac12 ] media/controls/doubletap-to-jump-backwards.html [ Crash Failure Pass Timeout ]
 crbug.com/783154 [ Mac Release ] media/controls/doubletap-to-jump-backwards.html [ Crash Failure Pass Timeout ]
 crbug.com/783154 [ Mac ] media/controls/doubletap-to-jump-forwards.html [ Crash Failure Pass Timeout ]
 crbug.com/783154 [ Mac ] media/controls/doubletap-to-jump-forwards-too-short.html [ Crash Failure Pass Timeout ]
@@ -4480,7 +4463,6 @@
 
 # Sheriff 2020-05-04
 crbug.com/952717 [ Debug Linux ] http/tests/xmlhttprequest/redirect-cross-origin-post.html [ Failure Pass ]
-crbug.com/952717 [ Debug Mac12 ] http/tests/xmlhttprequest/redirect-cross-origin-post.html [ Failure Pass ]
 
 # Sheriff 2020-05-18
 crbug.com/1084256 [ Linux ] http/tests/misc/insert-iframe-into-xml-document-before-xsl-transform.html [ Failure Pass ]
@@ -4491,7 +4473,6 @@
 crbug.com/1409975 virtual/threaded/external/wpt/scroll-animations/css/view-timeline-range-animation.html [ Failure Pass Timeout ]
 
 crbug.com/1071189 [ Debug Linux ] editing/selection/programmatic-selection-on-mac-is-directionless.html [ Pass Timeout ]
-crbug.com/1071189 [ Debug Mac12 ] editing/selection/programmatic-selection-on-mac-is-directionless.html [ Pass Timeout ]
 
 # Sheriff 2020-05-27
 crbug.com/1046784 http/tests/devtools/elements/styles/stylesheet-tracking.js [ Failure Pass Timeout ]
@@ -4630,7 +4611,6 @@
 crbug.com/1141206 scrollbars/overflow-scrollbar-combinations.html [ Failure Pass ]
 
 #Sheriff 2020-10-26
-crbug.com/1142877 [ Debug Mac12 ] external/wpt/mediacapture-fromelement/capture.html [ Crash Failure Pass ]
 crbug.com/1142877 [ Debug Linux ] external/wpt/mediacapture-fromelement/capture.html [ Crash Failure Pass ]
 
 # Sheriff 2020-11-04
@@ -5070,7 +5050,6 @@
 
 # Sheriff 2021-07-14
 crbug.com/1229039 [ Debug Linux ] external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass Timeout ]
-crbug.com/1229039 [ Debug Mac12 ] external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass Timeout ]
 
 # Test fails due to more accurate size reporting in heap snapshots.
 crbug.com/1229212 inspector-protocol/heap-profiler/heap-samples-in-snapshot.js [ Failure ]
@@ -5432,8 +5411,6 @@
 crbug.com/1292843 [ Mac11-arm64 Release ] external/wpt/css/css-transforms/animation/perspective-origin-interpolation.html [ Failure Pass ]
 crbug.com/1292869 [ Mac11-arm64 Release ] external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-cancel-and-hold.html [ Failure Pass ]
 crbug.com/1292871 [ Mac11-arm64 Release ] external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-dynamics-compressor-connections.html [ Failure Pass ]
-crbug.com/1292873 [ Debug Mac12 ] html/selectmenu/selectmenu-slot-warning-button.html [ Failure Pass ]
-crbug.com/1292874 [ Debug Mac12 ] html/selectmenu/selectmenu-slot-warning-listbox.html [ Failure Pass ]
 crbug.com/1292876 [ Mac ] http/tests/devtools/console/console-time.js [ Failure Pass ]
 
 crbug.com/1038139 [ Linux ] virtual/gpu-rasterization/images/2-comp.html [ Failure Pass ]
@@ -5826,12 +5803,9 @@
 # Disable scrolling flake on win
 crbug.com/1359541 [ Win ] virtual/threaded-prefer-compositing/fast/scrolling/scrollbars/mouse-scrolling-on-div-custom-scrollbar.html [ Failure Pass Timeout ]
 
-# Disable devtool flake on Mac11,12,10.15
-crbug.com/1363259 [ Debug Mac12 ] http/tests/devtools/network/network-initiator.js [ Failure Pass ]
 
 # Disable media security policy test on Linux (dbg, ASAN) and Mac12 (dbg)
 crbug.com/1349180 [ Linux ] external/wpt/content-security-policy/media-src/media-src-7_2_2.sub.html [ Failure Pass Timeout ]
-crbug.com/1349180 [ Debug Mac12 ] external/wpt/content-security-policy/media-src/media-src-7_2_2.sub.html [ Failure Pass Timeout ]
 
 # Disable flakes on mac builders
 crbug.com/1363708 [ Mac12 Release ] http/tests/media/gc-while-network-loading.html [ Pass Timeout ]
@@ -5841,7 +5815,6 @@
 
 # Disable DOM web flake on debug linux and mac
 crbug.com/1353578 [ Debug Linux ] fast/dom/gc-image-element-2.html [ Failure Pass ]
-crbug.com/1353578 [ Debug Mac12 ] fast/dom/gc-image-element-2.html [ Failure Pass ]
 
 # Disable web test flakes on linux (Debug Leak)
 crbug.com/1349576 [ Debug Linux ] external/wpt/pointerevents/pointerevent_mouse_capture_change_hover.html [ Failure Pass ]
@@ -5976,7 +5949,6 @@
 
 # Sheriff 2022-10-12
 
-crbug.com/1280883 [ Debug Mac12 ] virtual/compositor-threaded-percent-based-scrolling-dsf-2/virtual/percent-based-scrolling/max-percent-delta.html [ Failure Pass ]
 crbug.com/1280883 [ Debug Linux ] virtual/compositor-threaded-percent-based-scrolling-dsf-2/virtual/percent-based-scrolling/max-percent-delta.html [ Failure Pass ]
 crbug.com/1280883 [ Win ] virtual/compositor-threaded-percent-based-scrolling-dsf-2/virtual/percent-based-scrolling/max-percent-delta.html [ Failure Pass ]
 
@@ -6601,7 +6573,6 @@
 
 # Sheriff 2023-04-07
 crbug.com/1431236 external/wpt/long-animation-frame/tentative/loaf-user-callback.html [ Failure Pass ]
-crbug.com/1416265 external/wpt/js-self-profiling/class-names.https.html [ Crash Pass ]
 crbug.com/1416271 external/wpt/js-self-profiling/time-domain.window.html [ Crash Failure Pass ]
 
 # Flake 2023-04-07
@@ -6624,97 +6595,7 @@
 crbug.com/1426534 wpt_internal/content-security-policy/reporting-api/reporting-api-works-on-frame-ancestors.https.sub.html [ Failure Pass ]
 
 # Flake suppressor for Mac12 Tests (dbg) builder 2023-04-26
-[ Debug Mac12 ] compositing/video/video-controls-squashing.html [ Failure ]
-[ Debug Mac12 ] editing/selection/caret-at-bidi-boundary.html [ Failure ]
-[ Debug Mac12 ] external/wpt/content-security-policy/inheritance/history-iframe.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/cookies/attributes/domain.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/credential-management/fedcm-network-requests.https.html [ Failure ]
-[ Debug Mac12 ] external/wpt/css/css-text/text-transform/text-transform-capitalize-003.html [ Failure ]
-[ Debug Mac12 ] external/wpt/css/css-values/calc-in-media-queries-with-mixed-units.html [ Failure ]
-[ Debug Mac12 ] external/wpt/feature-policy/feature-policy-frame-policy-timing.https.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/fetch/local-network-access/iframe.tentative.https.window.html [ Failure ]
-[ Debug Mac12 ] external/wpt/fetch/local-network-access/mixed-content-fetch.tentative.https.window.html [ Failure ]
-[ Debug Mac12 ] external/wpt/fetch/metadata/generated/element-embed.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/fetch/metadata/generated/element-meta-refresh.optional.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?10-10 [ Failure ]
-[ Debug Mac12 ] external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?13-last [ Failure ]
-[ Debug Mac12 ] external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?8-8 [ Failure ]
-[ Debug Mac12 ] external/wpt/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-1.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-redirects.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/cross-origin-opener-policy/coop-sandbox-cuts-opener.https.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro_cross-origin.https.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/rendering/pixel-length-attributes.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/semantics/embedded-content/the-img-element/invisible-image.html [ Failure ]
-[ Debug Mac12 ] external/wpt/html/semantics/forms/the-input-element/focus-dynamic-type-change-on-blur.html [ Failure ]
-[ Debug Mac12 ] external/wpt/IndexedDB/idb-partitioned-coverage.tentative.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/longtask-timing/longtask-attributes.html [ Failure ]
-[ Debug Mac12 ] external/wpt/longtask-timing/longtask-in-childiframe-crossorigin.html [ Failure ]
-[ Debug Mac12 ] external/wpt/permissions-policy/permissions-policy-frame-policy-timing.https.sub.html [ Failure ]
-[ Debug Mac12 ] external/wpt/resource-timing/iframe-sequence-of-events.html [ Failure ]
-[ Debug Mac12 ] external/wpt/resource-timing/response-status-code.html [ Failure ]
-[ Debug Mac12 ] external/wpt/speculation-rules/prerender/response-code-successful.html?code=200 [ Failure ]
-[ Debug Mac12 ] external/wpt/speculation-rules/prerender/response-code-successful.html?code=202 [ Failure ]
-[ Debug Mac12 ] external/wpt/speculation-rules/prerender/response-code-successful.html?code=203 [ Failure ]
-[ Debug Mac12 ] external/wpt/upgrade-insecure-requests/link-upgrade.sub.https.html [ Failure ]
-[ Debug Mac12 ] external/wpt/webtransport/streams-close.https.any.html [ Failure ]
-[ Debug Mac12 ] external/wpt/webtransport/streams-close.https.any.serviceworker.html [ Failure ]
-[ Debug Mac12 ] external/wpt/webtransport/streams-close.https.any.sharedworker.html [ Failure ]
-[ Debug Mac12 ] external/wpt/webtransport/streams-close.https.any.worker.html [ Failure ]
-[ Debug Mac12 ] fast/dom/gc-acid3.html [ Failure ]
-[ Debug Mac12 ] fast/events/autoscroll-should-not-stop-on-keypress.html [ Failure ]
-[ Debug Mac12 ] fast/forms/range/range-stepup-stepdown-from-renderer.html [ Failure ]
-[ Debug Mac12 ] fast/loader/resource-with-idna-deviation-char-in-hostname.html [ Failure ]
-[ Debug Mac12 ] fast/peerconnection/RTCPeerConnection-undelivered-events-gc.html [ Failure ]
-[ Debug Mac12 ] gamepad/multiple-event-listeners.html [ Failure ]
-[ Debug Mac12 ] http/tests/devtools/network/preview-searchable.js [ Failure ]
-[ Debug Mac12 ] http/tests/devtools/sources/debugger-frameworks/frameworks-dom-xhr-event-breakpoints.js [ Failure ]
-[ Debug Mac12 ] http/tests/loading/image-picture-download-after-shrink.html [ Failure ]
-[ Debug Mac12 ] http/tests/media/video-progress-mediastream.html [ Failure ]
-[ Debug Mac12 ] http/tests/security/webgl-remote-read-remote-image-blocked-no-crossorigin.html [ Failure ]
-[ Debug Mac12 ] media/controls/doubletap-to-jump-backwards-at-start.html [ Failure ]
-[ Debug Mac12 ] media/video-source-error-no-candidate.html [ Failure ]
-[ Debug Mac12 ] media/video-source.html [ Failure ]
-[ Debug Mac12 ] virtual/attribution-reporting-debug-mode/wpt_internal/attribution-reporting/event-level-trigger-filter-data.sub.https.html?input=not_filters_no_matching [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/can-load-api.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-allow-all.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-allow-self.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-attribute-allow.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-unset.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/disallowed-navigations-dangling-markup-urn.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/get-nested-configs.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/sandbox-attribute.https.html [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?10-10 [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?13-last [ Failure ]
-[ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?8-8 [ Failure ]
-[ Debug Mac12 ] virtual/fledge/external/wpt/fledge/tentative/join-leave-ad-interest-group.https.sub.window.html [ Failure ]
-[ Debug Mac12 ] virtual/fledge/external/wpt/fledge/tentative/no-winner.https.sub.window.html [ Failure ]
-[ Debug Mac12 ] virtual/fledge/external/wpt/fledge/tentative/register-ad-beacon.https.sub.window.html [ Failure ]
-[ Debug Mac12 ] virtual/fledge/external/wpt/fledge/tentative/reporting-arguments.https.sub.window.html [ Failure ]
-[ Debug Mac12 ] virtual/fledge/external/wpt/fledge/tentative/trusted-bidding-signals.https.sub.window.html [ Failure ]
-[ Debug Mac12 ] virtual/gpu/external/wpt/mediacapture-record/MediaRecorder-canvas-media-source.https.html [ Failure ]
-[ Debug Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/local-network-access/mixed-content-fetch.tentative.https.window.html [ Failure ]
-[ Debug Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/element-frame.https.sub.html [ Failure ]
-[ Debug Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/element-iframe.sub.html [ Failure ]
-[ Debug Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html [ Failure ]
-[ Debug Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/element-meta-refresh.optional.sub.html [ Failure ]
-[ Debug Mac12 ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/element-picture.https.sub.html [ Failure ]
-[ Debug Mac12 ] virtual/lna-permission/external/wpt/fetch/local-network-access/mixed-content-fetch.tentative.https.window.html [ Failure ]
-[ Debug Mac12 ] virtual/lna-workers-disabled/external/wpt/fetch/local-network-access/mixed-content-fetch.tentative.https.window.html [ Failure ]
-[ Debug Mac12 ] virtual/lna-workers-enabled/external/wpt/fetch/local-network-access/mixed-content-fetch.tentative.https.window.html [ Failure ]
-[ Debug Mac12 ] virtual/oopr-canvas2d/external/wpt/mediacapture-record/MediaRecorder-canvas-media-source.https.html [ Failure ]
-[ Debug Mac12 ] virtual/partitioned-cookies/external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?10-10 [ Failure ]
-[ Debug Mac12 ] virtual/partitioned-cookies/external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?13-last [ Failure ]
-[ Debug Mac12 ] virtual/partitioned-cookies/external/wpt/html/anonymous-iframe/embedding.tentative.https.window.html?8-8 [ Failure ]
-[ Debug Mac12 ] virtual/prerender/external/wpt/speculation-rules/prerender/response-code-successful.html?code=202 [ Failure ]
-[ Debug Mac12 ] virtual/schemeful-same-site/external/wpt/cookies/attributes/domain.sub.html [ Failure ]
-[ Debug Mac12 ] virtual/shared-storage-fenced-frame-mparch-selecturl-limit/wpt_internal/shared_storage_selecturl_limit/run-url-selection-operation-limit-multiple-origins.https.html [ Failure ]
-[ Debug Mac12 ] virtual/shared-storage-fenced-frame-mparch/wpt_internal/shared_storage/embedder-context.https.html [ Failure ]
 crbug.com/1446992 [ Mac ] virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF.html [ Failure ]
-[ Debug Mac12 ] virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/basic.any.worker.html [ Failure ]
-[ Debug Mac12 ] virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-2.html [ Failure ]
-[ Debug Mac12 ] virtual/unified-autoplay/external/wpt/feature-policy/feature-policy-frame-policy-timing.https.sub.html [ Failure ]
 
 # Sheriff 2023-05-01
 crbug.com/1432145 [ Mac10.14 ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaStreamTrack-MediaElement-disabled-video-is-black.https.html [ Failure Timeout ]
@@ -6858,25 +6739,6 @@
 
 crbug.com/1448011 http/tests/devtools/application-panel/resources-panel-iframe-idb.js [ Failure Pass ]
 
-crbug.com/1448011 http/tests/devtools/application-panel/resources-panel-idb-clear-for-storage-key.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/application-panel/resources-panel-on-navigation.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/application-panel/resources-panel-selection-on-reload.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/database-data.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/database-names.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/database-refresh-view.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/database-structure.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/database-version-number.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/delete-entry.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/live-update-indexeddb-content.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/resources-panel.js [ Failure Pass Timeout ]
-crbug.com/1448011 http/tests/devtools/indexeddb/upgrade-events.js [ Failure Pass Timeout ]
-
 # Sheriff 2023-05-30
 crbug.com/1450020 [ Mac ] compositing/geometry/preserve-3d-switching.html [ Failure Pass ]
 
-crbug.com/1406017 http/tests/devtools/cache-storage/cache-data.js [ Failure Pass Timeout ]
-crbug.com/1406017 http/tests/devtools/cache-storage/cache-deletion.js [ Failure Pass Timeout ]
-crbug.com/1406017 http/tests/devtools/cache-storage/cache-entry-deletion.js [ Failure Pass Timeout ]
-crbug.com/1406017 http/tests/devtools/cache-storage/cache-live-update-cache-content.js [ Failure Pass Timeout ]
-crbug.com/1406017 http/tests/devtools/cache-storage/cache-names.js [ Failure Pass Timeout ]
-
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 54fb1d0d..bf243b3 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -366,7 +366,7 @@
               "wpt_internal/compute-pressure"],
     "exclusive_tests": "ALL",
     "args": ["--enable-features=ComputePressure"],
-    "expires": "Jul 1, 2023"
+    "expires": "Feb 1, 2024"
   },
   {
     "prefix": "highdpi-threaded",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-computed-expected.txt
index 3af63b1..21f4b77 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-computed-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-computed-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Property word-boundary-detection value 'normal' computes to 'normal' assert_true: word-boundary-detection doesn't seem to be supported in the computed style expected true got false
-FAIL Property word-boundary-detection value 'manual' computes to 'manual' assert_true: word-boundary-detection doesn't seem to be supported in the computed style expected true got false
+PASS Property word-boundary-detection value 'normal'
+FAIL Property word-boundary-detection value 'manual' assert_true: 'manual' is a supported value for word-boundary-detection. expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-valid-expected.txt
index ea86373..629fc74 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-valid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/parsing/word-boundary-detection-valid-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL e.style['word-boundary-detection'] = "normal" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['word-boundary-detection'] = "normal" should set the property value
 FAIL e.style['word-boundary-detection'] = "manual" should set the property value assert_not_equals: property should be set got disallowed value ""
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-path-interpolation-005.html b/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-path-interpolation-005.html
index 9924106..13de65d8 100644
--- a/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-path-interpolation-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-path-interpolation-005.html
@@ -110,6 +110,32 @@
         {at: 2, expect: 'ray(-90deg farthest-corner)'},
       ]);
 
+      test_interpolation({
+        property: 'offset-path',
+        from: 'ray(-10deg farthest-corner at 0% 0px)',
+        to: 'ray(-50deg farthest-corner at 100% 100px)'
+      }, [
+        {at: -1, expect: 'ray(30deg farthest-corner at -100% -100px)'},
+        {at: 0, expect: 'ray(-10deg farthest-corner at 0% 0px)'},
+        {at: 0.125, expect: 'ray(-15deg farthest-corner at 12.5% 12.5px)'},
+        {at: 0.875, expect: 'ray(-45deg farthest-corner at 87.5% 87.5px)'},
+        {at: 1, expect: 'ray(-50deg farthest-corner at 100% 100px)'},
+        {at: 2, expect: 'ray(-90deg farthest-corner at 200% 200px)'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        from: 'ray(-10deg farthest-corner)',
+        to: 'ray(-50deg farthest-corner at 100% 100%)'
+      }, [
+        {at: -1, expect: 'ray(30deg farthest-corner at 0% 0%)'},
+        {at: 0, expect: 'ray(-10deg farthest-corner)'},
+        {at: 0.125, expect: 'ray(-15deg farthest-corner at 56.25% 56.25%)'},
+        {at: 0.875, expect: 'ray(-45deg farthest-corner at 93.75% 93.75%)'},
+        {at: 1, expect: 'ray(-50deg farthest-corner at 100% 100%)'},
+        {at: 2, expect: 'ray(-90deg farthest-corner at 150% 150%)'},
+      ]);
+
       // No interpolation between different sizes and/or different containment.
       test_no_interpolation({
         property: 'offset-path',
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/blob_object_url.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/blob_object_url.html
new file mode 100644
index 0000000..fe673d86
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/blob_object_url.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Same-origin prerendering page can create a url for the given
+objects</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+<body>
+<script>
+
+setup(() => assertSpeculationRulesIsSupported());
+
+promise_test(async t => {
+  const {exec} = await create_prerendered_page(t);
+
+  const result = await exec(async () => {
+    const blob_contents = "test blob contents";
+    const blob = new Blob([blob_contents]);
+    const url = URL.createObjectURL(blob);
+    const fetched_content = await fetch(url).then(response => response.text());
+    URL.revokeObjectURL(url);
+    return fetched_content === blob_contents ? "PASS" : "FAIL";
+  });
+
+  // Start prerendering a page that attempts to create a url for a blob.
+  assert_equals(
+    result, "PASS",
+    'prerendering page should be able to create a url for blob and fetch it.');
+}, 'prerendering page should be able create url');
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 34845f4..9ad7e28 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -388,6 +388,7 @@
 widows: 2
 width: 769px
 will-change: auto
+word-boundary-detection: normal
 word-break: normal
 word-spacing: 0px
 writing-mode: horizontal-tb
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 20daa02..ca0b213 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -388,6 +388,7 @@
 widows: 2
 width: auto
 will-change: auto
+word-boundary-detection: normal
 word-break: normal
 word-spacing: 0px
 writing-mode: horizontal-tb
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/double-tap-select-prevent-default.html b/third_party/blink/web_tests/fast/events/touch/gesture/double-tap-select-prevent-default.html
new file mode 100644
index 0000000..48232b6
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/double-tap-select-prevent-default.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<script src='../../../../resources/gesture-util.js'></script>
+<script src='../../../../resources/testharness.js'></script>
+<script src='../../../../resources/testharnessreport.js'></script>
+
+<style>
+div {
+  height: 30px;
+  width: 500px;
+}
+</style>
+
+<div id='default'>
+<p contenteditable>
+This text can be selected.
+</p>
+</div>
+
+<div id='preventdefaultpointerdown'>
+<p contenteditable>
+Canceling pointerdown should prevent double-tap from selecting text.
+</p>
+</div>
+
+<div id='preventdefaulttouchstart'>
+<p contenteditable>
+Canceling touchstart should also prevent double-tap from selecting text.
+</p>
+</div>
+
+<div id='preventdefaultmousedown'>
+<p contenteditable>
+Canceling mousedown should also prevent double-tap from selecting text.
+</p>
+</div>
+
+<script>
+document.getElementById("preventdefaultpointerdown").addEventListener("pointerdown", function(e) {
+  e.preventDefault();
+});
+document.getElementById("preventdefaulttouchstart").addEventListener("touchstart", function(e) {
+  e.preventDefault();
+});
+document.getElementById("preventdefaultmousedown").addEventListener("mousedown", function(e) {
+  e.preventDefault();
+});
+
+let selection_changed = false;
+document.addEventListener("selectionchange", e => {
+  selection_changed = true;
+});
+
+// Test that editable text can be selected by double-tapping.
+promise_test(async () => {
+  window.getSelection().empty();
+  await waitForCompositorCommit();
+  selection_changed = false;
+
+  var rect = document.getElementById("default").getBoundingClientRect();
+  await doubleTapAt(rect.left + 10, rect.top + 10);
+  await conditionHolds(
+      () => { return window.getSelection().type != "None"; },
+      "Double-tap did not select anything.");
+
+  assert_true(selection_changed);
+}, "double-tap selection");
+
+// Test that double-tap selection can be prevented using preventDefault on pointerdown.
+promise_test(async () => {
+  window.getSelection().empty();
+  await waitForCompositorCommit();
+  selection_changed = false;
+
+  var rect = document.getElementById("preventdefaultpointerdown").getBoundingClientRect();
+  await doubleTapAt(rect.left + 10, rect.top + 10);
+
+  assert_false(selection_changed);
+  assert_equals(window.getSelection().type, "None");
+}, "preventDefault pointerdown prevents double-tap selection");
+
+// Test that double-tap selection can be prevented using preventDefault on touchstart.
+promise_test(async () => {
+  window.getSelection().empty();
+  await waitForCompositorCommit();
+  selection_changed = false;
+
+  var rect = document.getElementById("preventdefaulttouchstart").getBoundingClientRect();
+  await doubleTapAt(rect.left + 10, rect.top + 10);
+
+  assert_false(selection_changed);
+  assert_equals(window.getSelection().type, "None");
+}, "preventDefault touchstart prevents double-tap selection");
+
+// Test that double-tap selection can be prevented using preventDefault on mousedown.
+promise_test(async () => {
+  window.getSelection().empty();
+  await waitForCompositorCommit();
+  selection_changed = false;
+
+  var rect = document.getElementById("preventdefaultmousedown").getBoundingClientRect();
+  await doubleTapAt(rect.left + 10, rect.top + 10);
+
+  assert_false(selection_changed);
+  assert_equals(window.getSelection().type, "None");
+}, "preventDefault mousedown prevents double-tap selection");
+</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-idb-clear-for-storage-key.js b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-idb-clear-for-storage-key.js
index c27695b4..a2f9e1e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-idb-clear-for-storage-key.js
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-idb-clear-for-storage-key.js
@@ -34,7 +34,7 @@
     TestRunner.addResult('Database Length: ' + databases.length);
     TestRunner.addResult('Database Entries:');
     for (let j = 0; j < databases.length; ++j)
-      TestRunner.addResult(`  Storage key:${databases[j].storageKey}, Database Name:${databases[j].name}`);
+      TestRunner.addResult(`  Storage key:${databases[j].storageBucket.storageKey}, Database Name:${databases[j].name}`);
     TestRunner.addResult('**done**\n');
   }
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-data.js b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-data.js
index c4f91b4..ac24cd6b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-data.js
+++ b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-data.js
@@ -51,5 +51,5 @@
         .catch(errorAndExit);
   }
 
-  ApplicationTestRunner.waitForCacheRefresh(main);
+  main();
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-deletion.js b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-deletion.js
index df70c0f..b285b73 100644
--- a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-deletion.js
+++ b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-deletion.js
@@ -41,5 +41,5 @@
         .catch(errorAndExit);
   }
 
-  ApplicationTestRunner.waitForCacheRefresh(main);
+  main();
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-entry-deletion.js b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-entry-deletion.js
index f91567ef..7cf946d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-entry-deletion.js
+++ b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-entry-deletion.js
@@ -40,5 +40,5 @@
         .catch(errorAndExit);
   }
 
-  ApplicationTestRunner.waitForCacheRefresh(main);
+  main();
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-live-update-cache-content.js b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-live-update-cache-content.js
index 9d1932d..d3350da9 100644
--- a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-live-update-cache-content.js
+++ b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-live-update-cache-content.js
@@ -16,7 +16,6 @@
   var cacheStorageModel = TestRunner.mainTarget.model(SDK.ServiceWorkerCacheModel);
   cacheStorageModel.enable();
 
-  await new Promise(resolve => ApplicationTestRunner.waitForCacheRefresh(resolve));
   await ApplicationTestRunner.clearAllCaches();
   await ApplicationTestRunner.dumpCacheTree();
   await ApplicationTestRunner.createCache('testCache1');
diff --git a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-names.js b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-names.js
index 92d0a59..f022911 100644
--- a/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-names.js
+++ b/third_party/blink/web_tests/http/tests/devtools/cache-storage/cache-names.js
@@ -36,5 +36,5 @@
         .catch(errorAndExit);
   }
 
-  ApplicationTestRunner.waitForCacheRefresh(main);
+  main();
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/change-iframe-src.js b/third_party/blink/web_tests/http/tests/devtools/change-iframe-src.js
index 8e5c948..88e8c7bc 100644
--- a/third_party/blink/web_tests/http/tests/devtools/change-iframe-src.js
+++ b/third_party/blink/web_tests/http/tests/devtools/change-iframe-src.js
@@ -2,11 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that Elements panel allows to change src attribute on iframes inside inspected page. See bug 41350.\n`);
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('elements');
+  await TestRunner.loadLegacyModule('console');
 
   var messagePromise = new Promise(x => ConsoleTestRunner.addConsoleSniffer(x));
   await TestRunner.loadHTML(`
diff --git a/third_party/blink/web_tests/http/tests/devtools/command-line-api-inspect.js b/third_party/blink/web_tests/http/tests/devtools/command-line-api-inspect.js
index ef5854d..9184e6c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/command-line-api-inspect.js
+++ b/third_party/blink/web_tests/http/tests/devtools/command-line-api-inspect.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that inspect() command line api works.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.loadHTML(`
       <p id="p1">
       </p>
diff --git a/third_party/blink/web_tests/http/tests/devtools/compiler-script-mapping.js b/third_party/blink/web_tests/http/tests/devtools/compiler-script-mapping.js
index 745a81757..4c11072 100644
--- a/third_party/blink/web_tests/http/tests/devtools/compiler-script-mapping.js
+++ b/third_party/blink/web_tests/http/tests/devtools/compiler-script-mapping.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {SourcesTestRunner} from 'sources_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests SourceMap and CompilerScriptMapping.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
+  await TestRunner.loadLegacyModule('sources');
   await TestRunner.evaluateInPagePromise(`
       function addScript(url)
       {
diff --git a/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug-expected.txt b/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug-expected.txt
index 4f90209..d452f740 100644
--- a/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug-expected.txt
@@ -6,6 +6,6 @@
 Script execution paused.
 Call stack:
     0) handleClick (add-elements.js:15)
-    1) clickButton (compiler-source-mapping-debug.js:18)
+    1) clickButton (compiler-source-mapping-debug.js:22)
 Script execution resumed.
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug.js b/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug.js
index 317fa84..161085ed 100644
--- a/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug.js
+++ b/third_party/blink/web_tests/http/tests/devtools/compiler-source-mapping-debug.js
@@ -2,11 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {SourcesTestRunner} from 'sources_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests installing compiler source map in scripts panel.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.loadTestModule('network_test_runner');
+  await TestRunner.loadLegacyModule('sources');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.showPanel('sources');
 
   await TestRunner.addScriptTag('resources/compiled-2.js');
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-cd.js b/third_party/blink/web_tests/http/tests/devtools/console-cd.js
index de4767ba..750fe0e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console-cd.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console-cd.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that console evaluation can be performed in an iframe context.Bug 19872.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.addIframe("http://localhost:8000/devtools/resources/console-cd-iframe.html", {
     name: "myIFrame"
   });
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-document-write-from-external-script-logging.js b/third_party/blink/web_tests/http/tests/devtools/console-document-write-from-external-script-logging.js
index 25eec0d..764c344 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console-document-write-from-external-script-logging.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console-document-write-from-external-script-logging.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that ignored document.write() called from an external asynchronously loaded script is reported to console as a warning\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.evaluateInPagePromise(`
       function loadExternalScript()
       {
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-show-all-messages.js b/third_party/blink/web_tests/http/tests/devtools/console-show-all-messages.js
index aa92143..96a8925 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console-show-all-messages.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console-show-all-messages.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that console shows messages only from specific context when show target checkbox is checked.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.showPanel('console');
   await TestRunner.evaluateInPagePromise(`
       console.log("message from page!");
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-async.js b/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-async.js
index 2e5514d..dca004c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-async.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-async.js
@@ -2,11 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that XMLHttpRequest Logging works when Enabled and doesn't show logs when Disabled for asynchronous XHRs.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.loadTestModule('network_test_runner');
+  await TestRunner.loadLegacyModule('console');
 
   step1();
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-expected.txt
index 6ae54e1..4a106b5 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging-expected.txt
@@ -5,83 +5,83 @@
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
-(anonymous) @ console-xhr-logging.js:20
-console-xhr-logging.js:13 sending a GET request to resources/xhr-exists.html
+requestHelper @ console-xhr-logging.js:17
+(anonymous) @ console-xhr-logging.js:23
+console-xhr-logging.js:16 sending a GET request to resources/xhr-exists.html
 
 Message count: 3
 VM:37 GET http://127.0.0.1:8000/devtools/resources/xhr-does-not-exist.html 404 (Not Found)
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
-(anonymous) @ console-xhr-logging.js:26
+requestHelper @ console-xhr-logging.js:17
+(anonymous) @ console-xhr-logging.js:29
 VM:37 XHR failed loading: GET "http://127.0.0.1:8000/devtools/resources/xhr-does-not-exist.html".
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
-(anonymous) @ console-xhr-logging.js:26
-console-xhr-logging.js:13 sending a GET request to resources/xhr-does-not-exist.html
+requestHelper @ console-xhr-logging.js:17
+(anonymous) @ console-xhr-logging.js:29
+console-xhr-logging.js:16 sending a GET request to resources/xhr-does-not-exist.html
 
 Message count: 2
 VM:37 XHR finished loading: POST "http://127.0.0.1:8000/devtools/resources/post-target.cgi".
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
-console-xhr-logging.js:13 sending a POST request to resources/post-target.cgi
+console-xhr-logging.js:16 sending a POST request to resources/post-target.cgi
 
 Message count: 4
 VM:37 Access to XMLHttpRequest at 'http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
 VM:37 GET http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html net::ERR_FAILED 200 (OK)
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
 VM:37 XHR failed loading: GET "http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html".
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
-console-xhr-logging.js:13 sending a GET request to http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html
+console-xhr-logging.js:16 sending a GET request to http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html
 
 Message count: 1
-console-xhr-logging.js:13 sending a GET request to resources/xhr-exists.html
+console-xhr-logging.js:16 sending a GET request to resources/xhr-exists.html
 
 Message count: 2
 VM:37 GET http://127.0.0.1:8000/devtools/resources/xhr-does-not-exist.html 404 (Not Found)
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
-console-xhr-logging.js:13 sending a GET request to resources/xhr-does-not-exist.html
+console-xhr-logging.js:16 sending a GET request to resources/xhr-does-not-exist.html
 
 Message count: 1
-console-xhr-logging.js:13 sending a POST request to resources/post-target.cgi
+console-xhr-logging.js:16 sending a POST request to resources/post-target.cgi
 
 Message count: 3
 VM:37 Access to XMLHttpRequest at 'http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
 VM:37 GET http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html net::ERR_FAILED 200 (OK)
 makeXHR @ VM:37
 makeSimpleXHRWithPayload @ VM:13
 makeSimpleXHR @ VM:9
-requestHelper @ console-xhr-logging.js:14
+requestHelper @ console-xhr-logging.js:17
 (anonymous) @ VM:1
-console-xhr-logging.js:13 sending a GET request to http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html
+console-xhr-logging.js:16 sending a GET request to http://localhost:8000/devtools/resources/cors-disabled/xhr-exists.html
 
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging.js b/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging.js
index dde07fb..2df12ed 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console-xhr-logging.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that XMLHttpRequest Logging works when Enabled and doesn't show logs when Disabled.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.loadTestModule('network_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.evaluateInPagePromise(`
       function requestHelper(method, url)
       {
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-clear.js b/third_party/blink/web_tests/http/tests/devtools/console/console-clear.js
index 8b6d1f9..01ef081 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-clear.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console/console-clear.js
@@ -3,12 +3,12 @@
 // found in the LICENSE file.
 
 import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
 
 (async function() {
   TestRunner.addResult("Tests that console is cleared upon requestClearMessages call.\n");
 
   await TestRunner.showPanel("console");
-  await TestRunner.loadTestModule("console_test_runner");
 
   await TestRunner.evaluateInPagePromise(`
     console.log("one");
@@ -25,4 +25,4 @@
     await ConsoleTestRunner.dumpConsoleMessages();
     TestRunner.completeTest();
   }
-})();
\ No newline at end of file
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/copy-network-request.js b/third_party/blink/web_tests/http/tests/devtools/copy-network-request.js
index bbbab3e1..c5be6bb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/copy-network-request.js
+++ b/third_party/blink/web_tests/http/tests/devtools/copy-network-request.js
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   'use strict';
   TestRunner.addResult(`Tests curl command generation\n`);
-  await TestRunner.loadTestModule('network_test_runner');
   await TestRunner.showPanel('network');
 
   var logView = UI.panels.network.networkLogView;
diff --git a/third_party/blink/web_tests/http/tests/devtools/database-table-name-excaping.js b/third_party/blink/web_tests/http/tests/devtools/database-table-name-excaping.js
index da5d517f..bd85587 100644
--- a/third_party/blink/web_tests/http/tests/devtools/database-table-name-excaping.js
+++ b/third_party/blink/web_tests/http/tests/devtools/database-table-name-excaping.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ApplicationTestRunner} from 'application_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests how table names are escaped in database table view.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('application_test_runner');
+  await TestRunner.loadLegacyModule('console');
 
   var tableName = 'table-name-with-dashes-and-"quotes"';
   var escapedTableName = Resources.DatabaseTableView.prototype.escapeTableName(tableName, '', true);
diff --git a/third_party/blink/web_tests/http/tests/devtools/device-orientation-success-expected.txt b/third_party/blink/web_tests/http/tests/devtools/device-orientation-success-expected.txt
index 1562b9c..57219bbe 100644
--- a/third_party/blink/web_tests/http/tests/devtools/device-orientation-success-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/device-orientation-success-expected.txt
@@ -10,12 +10,12 @@
 Running: secondOrientationOverride
 
 Running: clearOverride
-device-orientation-success.js:30 alpha: 1.1 beta: 2.2 gamma: 3.3
-device-orientation-success.js:30 alpha: 20 beta: 30 gamma: 40
+device-orientation-success.js:33 alpha: 1.1 beta: 2.2 gamma: 3.3
+device-orientation-success.js:33 alpha: 20 beta: 30 gamma: 40
 inspected-page.html:1 A reload is required so that the existing AbsoluteOrientationSensor and RelativeOrientationSensor objects on this page use the overridden values that have been provided. Close the inspector and reload again to return to the normal behavior.
-device-orientation-success.js:30 alpha: 90 beta: 0 gamma: 0
-device-orientation-success.js:37 quaternion: 0.000000,0.000000,0.707107,0.707107
-device-orientation-success.js:30 alpha: 1.1 beta: 2.2 gamma: 3.3
+device-orientation-success.js:33 alpha: 90 beta: 0 gamma: 0
+device-orientation-success.js:40 quaternion: 0.000000,0.000000,0.707107,0.707107
+device-orientation-success.js:33 alpha: 1.1 beta: 2.2 gamma: 3.3
 
 Running: reloadPageAndOverride
 Page reloaded.
diff --git a/third_party/blink/web_tests/http/tests/devtools/device-orientation-success.js b/third_party/blink/web_tests/http/tests/devtools/device-orientation-success.js
index cad5cbdf..b343eae0 100644
--- a/third_party/blink/web_tests/http/tests/devtools/device-orientation-success.js
+++ b/third_party/blink/web_tests/http/tests/devtools/device-orientation-success.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Test device orientation\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.addScriptTag('/resources/testharness.js');
   await TestRunner.evaluateInPagePromise(`
       var sensorProvider = null;
diff --git a/third_party/blink/web_tests/http/tests/devtools/diff-module.js b/third_party/blink/web_tests/http/tests/devtools/diff-module.js
index 9d17133..4f88774f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/diff-module.js
+++ b/third_party/blink/web_tests/http/tests/devtools/diff-module.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that the Diff module correctly diffs things.\n`);
   await TestRunner.loadLegacyModule('diff');
diff --git a/third_party/blink/web_tests/http/tests/devtools/emulate-focus.js b/third_party/blink/web_tests/http/tests/devtools/emulate-focus.js
index 2e7b8bbb4..3d9cc1f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/emulate-focus.js
+++ b/third_party/blink/web_tests/http/tests/devtools/emulate-focus.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that focus emulation works.\n`);
   await dumpPageFocus();
diff --git a/third_party/blink/web_tests/http/tests/devtools/evaluate-in-page.js b/third_party/blink/web_tests/http/tests/devtools/evaluate-in-page.js
index 9795e337..429ff78 100644
--- a/third_party/blink/web_tests/http/tests/devtools/evaluate-in-page.js
+++ b/third_party/blink/web_tests/http/tests/devtools/evaluate-in-page.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`This tests that layout test can evaluate scripts in the inspected page.\n`);
   await TestRunner.evaluateInPagePromise(`
diff --git a/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel-expected.txt b/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel-expected.txt
index 375fadc9..f9bdb0b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel-expected.txt
@@ -1,5 +1,5 @@
 Tests that FileReader's Blob request isn't shown in network panel.
 
 requests in the network panel: 0
-file-reader-with-network-panel.js:14 done
+file-reader-with-network-panel.js:17 done
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel.js b/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel.js
index 289e5748..a39542b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel.js
+++ b/third_party/blink/web_tests/http/tests/devtools/file-reader-with-network-panel.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that FileReader's Blob request isn't shown in network panel.\n`);
-  await TestRunner.loadTestModule('network_test_runner');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.evaluateInPagePromise(`
       function readBlob()
       {
diff --git a/third_party/blink/web_tests/http/tests/devtools/file-system-project.js b/third_party/blink/web_tests/http/tests/devtools/file-system-project.js
index cd80366..0fb71c7 100644
--- a/third_party/blink/web_tests/http/tests/devtools/file-system-project.js
+++ b/third_party/blink/web_tests/http/tests/devtools/file-system-project.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {SourcesTestRunner} from 'sources_test_runner';
+import {BindingsTestRunner} from 'bindings_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests file system project.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
-  await TestRunner.loadTestModule('bindings_test_runner');
+  await TestRunner.loadLegacyModule('sources');
   await TestRunner.showPanel('sources');
 
   function fileSystemUISourceCodes() {
diff --git a/third_party/blink/web_tests/http/tests/devtools/filtered-item-selection-dialog-rendering.js b/third_party/blink/web_tests/http/tests/devtools/filtered-item-selection-dialog-rendering.js
index 8631719..098516e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/filtered-item-selection-dialog-rendering.js
+++ b/third_party/blink/web_tests/http/tests/devtools/filtered-item-selection-dialog-rendering.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {SourcesTestRunner} from 'sources_test_runner';
+
 (async function() {
   TestRunner.addResult(`Verifies that SelectUISourceCodeDialog rendering works properly.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
+  await TestRunner.loadLegacyModule('sources');
   await TestRunner.evaluateInPagePromise(`    function dummy1() { }
       //# sourceURL=http://test/helloWorld12.js
     `);
diff --git a/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask-expected.txt b/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask-expected.txt
index 6676ce98..a72fdfe9 100644
--- a/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask-expected.txt
@@ -37,5 +37,5 @@
     startTime : <number>
     type : "Layout"
 }
-Text details for Layout: test://evaluations/0/forced-layout-in-microtask.js:21:36
+Text details for Layout: test://evaluations/0/forced-layout-in-microtask.js:24:36
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask.js b/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask.js
index f1f020bf..25183b1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask.js
+++ b/third_party/blink/web_tests/http/tests/devtools/forced-layout-in-microtask.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {PerformanceTestRunner} from 'performance_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that Layout record has correct locations of layout being invalidated and forced.\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
+  await TestRunner.loadLegacyModule('timeline');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
       <style>
diff --git a/third_party/blink/web_tests/http/tests/devtools/fragment.js b/third_party/blink/web_tests/http/tests/devtools/fragment.js
index cc0f0cb..1f57b9ca 100644
--- a/third_party/blink/web_tests/http/tests/devtools/fragment.js
+++ b/third_party/blink/web_tests/http/tests/devtools/fragment.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests fragment is stripped from url by resource and page agents.\n`);
-  await TestRunner.loadTestModule('network_test_runner');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.showPanel('network');
   await TestRunner.evaluateInPagePromise(`
       function loadIFrame()
diff --git a/third_party/blink/web_tests/http/tests/devtools/geolocation-emulation-tests.js b/third_party/blink/web_tests/http/tests/devtools/geolocation-emulation-tests.js
index 4d14bf7f..ea0d5ab 100644
--- a/third_party/blink/web_tests/http/tests/devtools/geolocation-emulation-tests.js
+++ b/third_party/blink/web_tests/http/tests/devtools/geolocation-emulation-tests.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that geolocation emulation with latitude and longitude works as expected.\n`);
 
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.navigatePromise('https://devtools.test:8443/devtools/network/resources/empty.html');
   await TestRunner.BrowserAgent.invoke_grantPermissions({
     origin: 'https://devtools.test:8443',
diff --git a/third_party/blink/web_tests/http/tests/devtools/har-importer.js b/third_party/blink/web_tests/http/tests/devtools/har-importer.js
index 7fa2163..bcec6eb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/har-importer.js
+++ b/third_party/blink/web_tests/http/tests/devtools/har-importer.js
@@ -2,11 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ApplicationTestRunner} from 'application_test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(
       'Verifies that imported HAR files create matching NetworkRequests');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('application_test_runner');
-  await TestRunner.loadTestModule('network_test_runner');
+  await TestRunner.loadLegacyModule('console');
   const harRoot = new HARImporter.HARRoot(harJson);
   const requests = HARImporter.Importer.requestsFromHARLog(harRoot.log);
   const formattedRequests = await Promise.all(requests.map(async request => {
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-data.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-data.js
index 3e9a2bd..439d0fbc 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-data.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-data.js
@@ -4,6 +4,7 @@
 
 import {TestRunner} from 'test_runner';
 import {ApplicationTestRunner} from 'application_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
 
 (async function() {
   TestRunner.addResult(
@@ -19,7 +20,7 @@
   var objectStoreName1 = 'testObjectStore1';
   var objectStoreName2 = 'testObjectStore2';
   var indexName = 'testIndexName';
-  var databaseId = new Resources.IndexedDBModel.DatabaseId(storageKey, databaseName);
+  var databaseId = new Resources.IndexedDBModel.DatabaseId({storageKey}, databaseName);
 
   /**
    * @param {number} count
@@ -90,7 +91,7 @@
     }
   }
 
-  TestRunner.addSniffer(Resources.IndexedDBModel.prototype, 'updateStorageKeyDatabaseNames', fillDatabase, false);
+  fillDatabase();
 
   function fillDatabase() {
     ApplicationTestRunner.createDatabase(mainFrameId, databaseName, step2);
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-names.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-names.js
index ed9d341a..280ba75 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-names.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-names.js
@@ -18,13 +18,17 @@
     TestRunner.addResult('Dumping database names:');
     var storageKeys = TestRunner.storageKeyManager.storageKeys();
     var storageKey = storageKeys[0];
-    var names = indexedDBModel.databaseNamesByStorageKey.get(storageKey);
-    for (let name of names || [])
-      TestRunner.addResult('    ' + name);
+    var buckets =
+        indexedDBModel.databaseNamesByStorageKeyAndBucket.get(storageKey) || [];
+    for (const [_bucketName, dbIds] of buckets) {
+      for (const dbId of dbIds) {
+        TestRunner.addResult('    ' + dbId.name);
+      }
+    }
     TestRunner.addResult('');
-  }
+ }
 
-  TestRunner.addSniffer(Resources.IndexedDBModel.prototype, 'updateStorageKeyDatabaseNames', step2, false);
+  step2();
 
   function step2() {
     dumpDatabaseNames();
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-refresh-view.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-refresh-view.js
index a3bf149..ac77a941 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-refresh-view.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-refresh-view.js
@@ -27,12 +27,9 @@
 
   function waitRefreshDatabase() {
     var view = UI.panels.resources.sidebar.indexedDBListTreeElement.idbDatabaseTreeElements[0].view;
-    const promise = new Promise((resolve) => {
-      TestRunner.addSniffer(Resources.IDBDatabaseView.prototype, 'updatedForTests', resolve, false);
-    });
 
     view.getComponent().refreshDatabaseButtonClicked();
-    return promise;
+    return indexedDBModel.once(Resources.IndexedDBModel.Events.DatabaseLoaded);
   }
 
   function waitRefreshDatabaseRightClick() {
@@ -65,7 +62,7 @@
   ApplicationTestRunner.dumpIndexedDBTree();
 
   // Create database
-  await ApplicationTestRunner.createDatabaseAsync(databaseName);
+  ApplicationTestRunner.createDatabaseAsync(databaseName);
   await new Promise(waitDatabaseAdded);
   var idbDatabaseTreeElement = UI.panels.resources.sidebar.indexedDBListTreeElement.idbDatabaseTreeElements[0];
   databaseId = idbDatabaseTreeElement.databaseId;
@@ -81,7 +78,6 @@
   // Create first objectstore
   await ApplicationTestRunner.createObjectStoreAsync(databaseName, objectStoreName1, indexName, keyPath);
   await waitRefreshDatabase();
-  await new Promise(resolve => setTimeout(resolve, 0));
   TestRunner.addResult('Created first objectstore.');
   ApplicationTestRunner.dumpIndexedDBTree();
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-structure.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-structure.js
index a1cbce1..0eba2d7 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-structure.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-structure.js
@@ -4,6 +4,7 @@
 
 import {TestRunner} from 'test_runner';
 import {ApplicationTestRunner} from 'application_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
 
 (async function() {
   TestRunner.addResult(`Tests that database names are correctly loaded and saved in IndexedDBModel.\n`);
@@ -15,7 +16,7 @@
   var mainFrameId = TestRunner.resourceTreeModel.mainFrame.id;
   var databaseName = 'testDatabase1';
   var storageKey = 'http://127.0.0.1:8000/';
-  var databaseId = new Resources.IndexedDBModel.DatabaseId(storageKey, databaseName);
+  var databaseId = new Resources.IndexedDBModel.DatabaseId({storageKey}, databaseName);
 
   function dumpDatabase() {
     TestRunner.addResult('Dumping database:');
@@ -46,7 +47,7 @@
     TestRunner.addResult('');
   }
 
-  TestRunner.addSniffer(Resources.IndexedDBModel.prototype, 'updateStorageKeyDatabaseNames', step2, false);
+  step2();
 
   function step2() {
     ApplicationTestRunner.createDatabase(mainFrameId, databaseName, step3);
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-version-number.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-version-number.js
index 145e488..8342367 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-version-number.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/database-version-number.js
@@ -4,6 +4,7 @@
 
 import {TestRunner} from 'test_runner';
 import {ApplicationTestRunner} from 'application_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
 
 (async function() {
   TestRunner.addResult(`Tests that database names are correctly loaded and saved in IndexedDBModel.\n`);
@@ -15,7 +16,7 @@
   var mainFrameId = TestRunner.resourceTreeModel.mainFrame.id;
   var databaseName = 'testDatabase1';
   var storageKey = 'http://127.0.0.1:8000/';
-  var databaseId = new Resources.IndexedDBModel.DatabaseId(storageKey, databaseName);
+  var databaseId = new Resources.IndexedDBModel.DatabaseId({storageKey}, databaseName);
 
   function dumpDatabase() {
     TestRunner.addResult('Dumping database:');
@@ -46,7 +47,7 @@
     TestRunner.addResult('');
 }
 
-  TestRunner.addSniffer(Resources.IndexedDBModel.prototype, 'updateStorageKeyDatabaseNames', step2, false);
+  step2();
 
   function step2() {
     ApplicationTestRunner.createDatabaseWithVersion(mainFrameId, databaseName, 2147483647, step3);
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/delete-entry.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/delete-entry.js
index 2fde27a3..4ff2f130 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/delete-entry.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/delete-entry.js
@@ -37,7 +37,6 @@
   await ApplicationTestRunner.createDatabaseAsync('database1');
   UI.panels.resources.sidebar.indexedDBListTreeElement.refreshIndexedDB();
   await databaseAddedPromise;
-  UI.panels.resources.sidebar.indexedDBListTreeElement.expand();
 
   var idbDatabaseTreeElement = UI.panels.resources.sidebar.indexedDBListTreeElement.idbDatabaseTreeElements[0];
   await ApplicationTestRunner.createObjectStoreAsync('database1', 'objectStore1', 'index1');
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/resources-panel.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/resources-panel.js
index 6afab4d8..029a850 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/resources-panel.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/resources-panel.js
@@ -93,8 +93,9 @@
     TestRunner.deprecatedRunAfterPendingDispatches(() => TestRunner.navigate(originalURL, navigatedBack));
   }
 
-  function navigatedBack() {
+  async function navigatedBack() {
     TestRunner.addResult('Navigated back.');
+    await new Promise(resolve => TestRunner.deprecatedRunAfterPendingDispatches(resolve));
     indexedDBModel.addEventListener(Resources.IndexedDBModel.Events.DatabaseLoaded, databaseLoaded2);
     UI.panels.resources.sidebar.indexedDBListTreeElement.refreshIndexedDB();
     TestRunner.addResult('Refreshing.');
diff --git a/third_party/blink/web_tests/http/tests/devtools/indexeddb/upgrade-events.js b/third_party/blink/web_tests/http/tests/devtools/indexeddb/upgrade-events.js
index 040a17a..8e7bb60 100644
--- a/third_party/blink/web_tests/http/tests/devtools/indexeddb/upgrade-events.js
+++ b/third_party/blink/web_tests/http/tests/devtools/indexeddb/upgrade-events.js
@@ -16,7 +16,7 @@
   var storageKey = 'http://127.0.0.1:8000/';
   var databaseName = 'testDatabase - ' + self.location;
   var objectStoreName = 'testObjectStore';
-  var databaseId = new Resources.IndexedDBModel.DatabaseId(storageKey, databaseName);
+  var databaseId = new Resources.IndexedDBModel.DatabaseId({storageKey}, databaseName);
 
   function onConsoleError(callback) {
     var old = console.error;
@@ -53,7 +53,7 @@
     }
   }
 
-  TestRunner.addSniffer(Resources.IndexedDBModel.prototype, 'updateStorageKeyDatabaseNames', fillDatabase, false);
+  fillDatabase();
 
   function fillDatabase() {
     TestRunner.addResult('Preparing database');
@@ -111,8 +111,8 @@
     indexedDBModel.refreshDatabaseNames();
 
     function step2() {
-      var names = indexedDBModel.databaseNamesByStorageKey.get(storageKey);
-      TestRunner.assertEquals(true, names.has(databaseName), 'Database should exist');
+      var names = indexedDBModel.databaseNamesByStorageKeyAndBucket.get(storageKey).get('');
+      TestRunner.assertEquals(true, !![...names].find(dbId => dbId.name === databaseName), 'Database should exist');
       callback();
     }
   }
@@ -122,8 +122,8 @@
     indexedDBModel.refreshDatabaseNames();
 
     function step2() {
-      var names = indexedDBModel.databaseNamesByStorageKey.get(storageKey);
-      TestRunner.assertEquals(false, names.has(databaseName), 'Database should not exist');
+      var names = indexedDBModel.databaseNamesByStorageKeyAndBucket.get(storageKey).get('');
+      TestRunner.assertEquals(false, !![...names].find(dbId => dbId.name === databaseName), 'Database should not exist');
       callback();
     }
   }
diff --git a/third_party/blink/web_tests/http/tests/devtools/inline-source-map-loading.js b/third_party/blink/web_tests/http/tests/devtools/inline-source-map-loading.js
index 70612f6..852402fb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/inline-source-map-loading.js
+++ b/third_party/blink/web_tests/http/tests/devtools/inline-source-map-loading.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Verify that inline sourcemap has proper URL and compiledURL.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/input-event-warning.js b/third_party/blink/web_tests/http/tests/devtools/input-event-warning.js
index e4b740b..17139fe 100644
--- a/third_party/blink/web_tests/http/tests/devtools/input-event-warning.js
+++ b/third_party/blink/web_tests/http/tests/devtools/input-event-warning.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that console warnings are issued for a blocked event listener and that there is no crash when an offending listener is removed by the handler.\n`);
diff --git a/third_party/blink/web_tests/http/tests/devtools/inspect-element.js b/third_party/blink/web_tests/http/tests/devtools/inspect-element.js
index 715c2c4e..e347761 100644
--- a/third_party/blink/web_tests/http/tests/devtools/inspect-element.js
+++ b/third_party/blink/web_tests/http/tests/devtools/inspect-element.js
@@ -2,11 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that inspect element action works for iframe children (https://bugs.webkit.org/show_bug.cgi?id=76808).\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
+  await TestRunner.loadLegacyModule('console');
+  await TestRunner.loadLegacyModule('elements');
   await TestRunner.showPanel('elements');
 
   await TestRunner.addIframe('resources/inspect-element-iframe.html');
diff --git a/third_party/blink/web_tests/http/tests/devtools/inspect-iframe-from-different-domain.js b/third_party/blink/web_tests/http/tests/devtools/inspect-iframe-from-different-domain.js
index a35ace0..e659036 100644
--- a/third_party/blink/web_tests/http/tests/devtools/inspect-iframe-from-different-domain.js
+++ b/third_party/blink/web_tests/http/tests/devtools/inspect-iframe-from-different-domain.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that style properties of elements in iframes loaded from domain different from the main document domain can be inspected. See bug 31587.\n`);
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
+  await TestRunner.loadLegacyModule('elements');
   await TestRunner.navigatePromise("http://example.test:8000/devtools/resources/empty.html");
   // NOTE: evaluateInPageAsync() waits on the promise at the end of block before
   // resolving the promise it returned. Other forms of the evaluate including
diff --git a/third_party/blink/web_tests/http/tests/devtools/inspected-objects-not-overriden.js b/third_party/blink/web_tests/http/tests/devtools/inspected-objects-not-overriden.js
index c517316e..cd03b63 100644
--- a/third_party/blink/web_tests/http/tests/devtools/inspected-objects-not-overriden.js
+++ b/third_party/blink/web_tests/http/tests/devtools/inspected-objects-not-overriden.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 /* TODO(chromium:1050549)
  * once that bug is complete we can lose this test
  * as DevTools will no longer touch built-in prototypes.
diff --git a/third_party/blink/web_tests/http/tests/devtools/inspector-backend-commands.js b/third_party/blink/web_tests/http/tests/devtools/inspector-backend-commands.js
index d5b631f..ff8c541 100644
--- a/third_party/blink/web_tests/http/tests/devtools/inspector-backend-commands.js
+++ b/third_party/blink/web_tests/http/tests/devtools/inspector-backend-commands.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests correctness of promisified protocol commands.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/load-file-resource-for-frontend.js b/third_party/blink/web_tests/http/tests/devtools/load-file-resource-for-frontend.js
index b66a9a85..0370f8f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/load-file-resource-for-frontend.js
+++ b/third_party/blink/web_tests/http/tests/devtools/load-file-resource-for-frontend.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Test loading file resource from front-end \n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/local-object-properties-section.js b/third_party/blink/web_tests/http/tests/devtools/local-object-properties-section.js
index c9f7628..2093a89a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/local-object-properties-section.js
+++ b/third_party/blink/web_tests/http/tests/devtools/local-object-properties-section.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Test that ObjectPropertiesSection works with local remote objects.\n`);
   await TestRunner.loadLegacyModule('ui/legacy/components/object_ui');
diff --git a/third_party/blink/web_tests/http/tests/devtools/local-object.js b/third_party/blink/web_tests/http/tests/devtools/local-object.js
index ecdb137..29ae366 100644
--- a/third_party/blink/web_tests/http/tests/devtools/local-object.js
+++ b/third_party/blink/web_tests/http/tests/devtools/local-object.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests callFunction on local remote objects.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/modify-cross-domain-rule.js b/third_party/blink/web_tests/http/tests/devtools/modify-cross-domain-rule.js
index fd5f0b15..791fd62 100644
--- a/third_party/blink/web_tests/http/tests/devtools/modify-cross-domain-rule.js
+++ b/third_party/blink/web_tests/http/tests/devtools/modify-cross-domain-rule.js
@@ -2,11 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+
 (async function() {
   'use strict';
   TestRunner.addResult(
       `Tests that modifying a rule in a stylesheet loaded from a different domain does not crash the renderer.\n`);
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
+  await TestRunner.loadLegacyModule('elements');
   await TestRunner.loadHTML(`
       <div id="inspected">Text</div>
     `);
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-xhr-async.js b/third_party/blink/web_tests/http/tests/devtools/network/network-xhr-async.js
index 0525e8e..d6149c6d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/network/network-xhr-async.js
+++ b/third_party/blink/web_tests/http/tests/devtools/network/network-xhr-async.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
 import {NetworkTestRunner} from 'network_test_runner';
 
 (async function() {
diff --git a/third_party/blink/web_tests/http/tests/devtools/overlay-frame-crash.js b/third_party/blink/web_tests/http/tests/devtools/overlay-frame-crash.js
index 76ce104..8a77f64 100644
--- a/third_party/blink/web_tests/http/tests/devtools/overlay-frame-crash.js
+++ b/third_party/blink/web_tests/http/tests/devtools/overlay-frame-crash.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+
 (async function() {
   TestRunner.addResult('Regression test for crbug.com/1058718\n');
 
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
+  await TestRunner.loadLegacyModule('elements');
   await TestRunner.showPanel('elements');
   await TestRunner.loadHTML(`<div id='foo'>foo</div>`);
   await TestRunner.addScriptTag('network/resources/gc.js');
diff --git a/third_party/blink/web_tests/http/tests/devtools/remote-object.js b/third_party/blink/web_tests/http/tests/devtools/remote-object.js
index e13deac..a92d270f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/remote-object.js
+++ b/third_party/blink/web_tests/http/tests/devtools/remote-object.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests formatting of different types of remote objects.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/report-API-errors.js b/third_party/blink/web_tests/http/tests/devtools/report-API-errors.js
index 8c819cf..fb33ff5 100644
--- a/third_party/blink/web_tests/http/tests/devtools/report-API-errors.js
+++ b/third_party/blink/web_tests/http/tests/devtools/report-API-errors.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that InspectorBackendStub is catching incorrect arguments.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/report-protocol-errors.js b/third_party/blink/web_tests/http/tests/devtools/report-protocol-errors.js
index b634fb2e..7a7dd171 100644
--- a/third_party/blink/web_tests/http/tests/devtools/report-protocol-errors.js
+++ b/third_party/blink/web_tests/http/tests/devtools/report-protocol-errors.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that InspectorBackendDispatcher is catching incorrect messages.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion.js b/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion.js
index 61df02b..b2c95e1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion.js
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+import {ApplicationTestRunner} from 'application_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests conversion of Inspector's resource representation into HAR format.\n`);
-  await TestRunner.loadTestModule('network_test_runner');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('application_test_runner');
+  await TestRunner.loadLegacyModule('console');
 
   await TestRunner.NetworkAgent.setCacheDisabled(true);
   await TestRunner.reloadPagePromise();
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
index 49dd91be..76a635d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
@@ -2,11 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ApplicationTestRunner} from 'application_test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   'use strict';
   TestRunner.addResult(`Tests the nondeterministic bits of HAR conversion via the magic of hard-coded values.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('application_test_runner');
-  await TestRunner.loadTestModule('network_test_runner');
+  await TestRunner.loadLegacyModule('console');
 
   function visibleNewlines(s) {
     return s.replace(/\r/, '\\r').replace(/\n/, '\\n');
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6.js b/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6.js
index c78d066..f3da50df 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6.js
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6.js
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that resources panel shows form data parameters.\n`);
-  await TestRunner.loadTestModule('network_test_runner');
   await TestRunner.navigatePromise('http://[::1]:8000/devtools/resources/inspected-page.html');
   await TestRunner.evaluateInPagePromise(`
       document.write(\`<form target="target-iframe" method="POST" action="http://[::1]:8000/devtools/resources/post-target.cgi?queryParam1=queryValue1&amp;queryParam2=#fragmentParam1=fragmentValue1&amp;fragmentParam2=">
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-parameters.js b/third_party/blink/web_tests/http/tests/devtools/resource-parameters.js
index 903b5481..fce14b6 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-parameters.js
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-parameters.js
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that resources panel shows form data parameters.\n`);
-  await TestRunner.loadTestModule('network_test_runner');
   await TestRunner.loadHTML(`
       <form target="target-iframe" method="POST" action="http://127.0.0.1:8000/devtools/resources/post-target.cgi?queryParam1=queryValue1&amp;queryParam2=#fragmentParam1=fragmentValue1&amp;fragmentParam2=">
       <input name="formParam1" value="formValue1">
diff --git a/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js b/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js
index 758c634d..581b68ba 100644
--- a/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js
+++ b/third_party/blink/web_tests/http/tests/devtools/reveal-objects.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests object revelation in the UI.\n`);
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
-  await TestRunner.loadTestModule('network_test_runner');
+  await TestRunner.loadLegacyModule('elements');
   await TestRunner.loadLegacyModule('sources');
   await TestRunner.loadLegacyModule('resources');
   await TestRunner.showPanel('elements');
diff --git a/third_party/blink/web_tests/http/tests/devtools/screen-orientation-override.js b/third_party/blink/web_tests/http/tests/devtools/screen-orientation-override.js
index 4c0b7bf3e..386ca491 100644
--- a/third_party/blink/web_tests/http/tests/devtools/screen-orientation-override.js
+++ b/third_party/blink/web_tests/http/tests/devtools/screen-orientation-override.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Test screen orientation override.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
 
   await TestRunner.navigatePromise('resources/screen-orientation-resource.html');
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/show-context-menu.js b/third_party/blink/web_tests/http/tests/devtools/show-context-menu.js
index 418e17b..0d448b6 100644
--- a/third_party/blink/web_tests/http/tests/devtools/show-context-menu.js
+++ b/third_party/blink/web_tests/http/tests/devtools/show-context-menu.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests type checks in DevToolsHost.showContextMenuAtPoint\n`);
   InspectorFrontendHost.showContextMenuAtPoint(1.1, 2.2, [0x41414141]);
diff --git a/third_party/blink/web_tests/http/tests/devtools/sourcemap-section-warning.js b/third_party/blink/web_tests/http/tests/devtools/sourcemap-section-warning.js
index f8f2a71..e1e9cfa 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sourcemap-section-warning.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sourcemap-section-warning.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`Verify that sourcemap emits warning if there's a section with "url" field.`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.showPanel('console');
   const url = 'http://127.0.0.1:8000/devtools/resources/source-map-warning.html';
   await TestRunner.navigatePromise(url);
diff --git a/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage-update.js b/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage-update.js
index 3986221..976536ff 100644
--- a/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage-update.js
+++ b/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage-update.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ApplicationTestRunner} from 'application_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Test that storage panel is present and that it contains correct data whenever localStorage is updated.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('application_test_runner');
+  await TestRunner.loadLegacyModule('console');
     // Note: every test that uses a storage API must manually clean-up state from previous tests.
   await ApplicationTestRunner.resetState();
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage.js b/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage.js
index 4e34175..3edb45ad 100644
--- a/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage.js
+++ b/third_party/blink/web_tests/http/tests/devtools/storage-panel-dom-storage.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ApplicationTestRunner} from 'application_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Test that storage panel is present and that it contains correct data for local and session DOM storages.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('application_test_runner');
+  await TestRunner.loadLegacyModule('console');
     // Note: every test that uses a storage API must manually clean-up state from previous tests.
   await ApplicationTestRunner.resetState();
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/stylesheet-source-mapping.js b/third_party/blink/web_tests/http/tests/devtools/stylesheet-source-mapping.js
index b0926e1..88002d53 100644
--- a/third_party/blink/web_tests/http/tests/devtools/stylesheet-source-mapping.js
+++ b/third_party/blink/web_tests/http/tests/devtools/stylesheet-source-mapping.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {SourcesTestRunner} from 'sources_test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests SourceMap and StyleSheetMapping.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
+  await TestRunner.loadLegacyModule('sources');
   await TestRunner.evaluateInPagePromise(`
       function addStyleSheet()
       {
diff --git a/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-closeable-persistence.js b/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-closeable-persistence.js
index 69a04ac..45478452 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-closeable-persistence.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-closeable-persistence.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests extensible tabbed pane closeable tabs persistence logic.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-max-tab-width-calculation.js b/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-max-tab-width-calculation.js
index fd457d8..c9d061b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-max-tab-width-calculation.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-max-tab-width-calculation.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests tabbed pane max tab element width calculation.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-tabs-to-show.js b/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-tabs-to-show.js
index 95615638..b9a04fa40 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-tabs-to-show.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tabbed-pane-tabs-to-show.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests tabbed pane tabs to show calculation.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/template-content-inspect-crash.js b/third_party/blink/web_tests/http/tests/devtools/template-content-inspect-crash.js
index 7bcab32..0cf6c67 100644
--- a/third_party/blink/web_tests/http/tests/devtools/template-content-inspect-crash.js
+++ b/third_party/blink/web_tests/http/tests/devtools/template-content-inspect-crash.js
@@ -2,10 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {ElementsTestRunner} from 'elements_test_runner';
+import {ConsoleTestRunner} from 'console_test_runner';
+
 (async function() {
   TestRunner.addResult(`This test verifies that template's content DocumentFragment is accessible from DevTools.\n`);
-  await TestRunner.loadLegacyModule('elements'); await TestRunner.loadTestModule('elements_test_runner');
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
+  await TestRunner.loadLegacyModule('elements');
+  await TestRunner.loadLegacyModule('console');
   await TestRunner.showPanel('elements');
   await TestRunner.loadHTML(`
       <p id="description"></p>
diff --git a/third_party/blink/web_tests/http/tests/devtools/text-autosizing-override.js b/third_party/blink/web_tests/http/tests/devtools/text-autosizing-override.js
index 44238cc..755a3704 100644
--- a/third_party/blink/web_tests/http/tests/devtools/text-autosizing-override.js
+++ b/third_party/blink/web_tests/http/tests/devtools/text-autosizing-override.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`This text should be autosized to 40px computed font-size (16 * 800/320).\n`);
   await TestRunner.loadHTML(`
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing-browser-thread.js b/third_party/blink/web_tests/http/tests/devtools/tracing-browser-thread.js
index e78c1c1..ab0c660 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing-browser-thread.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing-browser-thread.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {PerformanceTestRunner} from 'performance_test_runner';
+
 (async function() {
   TestRunner.addResult(`Test that tracing model correctly finds the main browser thread in the trace.\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
+  await TestRunner.loadLegacyModule('timeline');
 
   var sessionId = 1;
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing-model-storage.js b/third_party/blink/web_tests/http/tests/devtools/tracing-model-storage.js
index ed7ce53..c2a48ca 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing-model-storage.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing-model-storage.js
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {PerformanceTestRunner} from 'performance_test_runner';
+
 (async function() {
   TestRunner.addResult(`Test that tracing model correctly processes trace events.\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
+  await TestRunner.loadLegacyModule('timeline');
 
   var mainThread = 1;
   var pid = 100;
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing-session-id.js b/third_party/blink/web_tests/http/tests/devtools/tracing-session-id.js
index e56ffcd..c2048241 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing-session-id.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing-session-id.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {PerformanceTestRunner} from 'performance_test_runner';
+
 (async function() {
   TestRunner.addResult(
       `Tests that Tracing agent returns a session id upon a start that is matching one issued in trace events.\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
+  await TestRunner.loadLegacyModule('timeline');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
       <p style="transform: translateZ(10px)"> <!-- Force compositing so we have SetLayerTreeHostId event as well -->
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation-sgx.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation-sgx.js
index a60046ba..e0d3e995 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation-sgx.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation-sgx.js
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 import {TestRunner} from 'test_runner';
+import {PerformanceTestRunner} from 'performance_test_runner';
 
 (async function() {
   TestRunner.addResult(`Tests presence and order of tracing events for a browser navigation.\n`);
-  await TestRunner.loadTestModule(`performance_test_runner`);
   await TestRunner.showPanel(`timeline`);
   await TestRunner.NetworkAgent.setCacheDisabled(true);
   await TestRunner.navigatePromise('/loading/sxg/resources/sxg-location.sxg');
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation.js
index cd8415c..d30c2db 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-navigation.js
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 import {TestRunner} from 'test_runner';
+import {PerformanceTestRunner} from 'performance_test_runner';
 
 (async function() {
   TestRunner.addResult(`Tests presence and order of tracing events for a browser navigation.\n`);
-  await TestRunner.loadTestModule(`performance_test_runner`);
   await TestRunner.showPanel(`timeline`);
   await TestRunner.NetworkAgent.setCacheDisabled(true);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js
index caef1c71..159e8d5b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js
+++ b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting-major-version.js
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Test user agent setting\n`);
-  await TestRunner.loadTestModule('network_test_runner');
   await TestRunner.showPanel('network');
 
   const chromeRegex = new RegExp('(?:^|\\W)Chrome/(\\S+)');
diff --git a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js
index 81cf216..15cfccb1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js
+++ b/third_party/blink/web_tests/http/tests/devtools/user-agent-setting.js
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {NetworkTestRunner} from 'network_test_runner';
+
 (async function() {
   TestRunner.addResult(`Test user agent setting\n`);
-  await TestRunner.loadTestModule('network_test_runner');
 
   var cases = [
     'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36',
diff --git a/third_party/blink/web_tests/http/tests/devtools/version-controller.js b/third_party/blink/web_tests/http/tests/devtools/version-controller.js
index 357053dd..c859b2a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/version-controller.js
+++ b/third_party/blink/web_tests/http/tests/devtools/version-controller.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests inspector version controller.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/workers-on-navigation.js b/third_party/blink/web_tests/http/tests/devtools/workers-on-navigation.js
index 77b240f..41a9a81 100644
--- a/third_party/blink/web_tests/http/tests/devtools/workers-on-navigation.js
+++ b/third_party/blink/web_tests/http/tests/devtools/workers-on-navigation.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests that workers are correctly detached upon navigation.\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/workspace-mapping.js b/third_party/blink/web_tests/http/tests/devtools/workspace-mapping.js
index bb175e0..71799896 100644
--- a/third_party/blink/web_tests/http/tests/devtools/workspace-mapping.js
+++ b/third_party/blink/web_tests/http/tests/devtools/workspace-mapping.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+
 (async function() {
   TestRunner.addResult(`Tests workspace mappings\n`);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/workspace-view-file-system-header.js b/third_party/blink/web_tests/http/tests/devtools/workspace-view-file-system-header.js
index e405b224..86128591 100644
--- a/third_party/blink/web_tests/http/tests/devtools/workspace-view-file-system-header.js
+++ b/third_party/blink/web_tests/http/tests/devtools/workspace-view-file-system-header.js
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {TestRunner} from 'test_runner';
+import {BindingsTestRunner} from 'bindings_test_runner';
+
 (async function() {
   TestRunner.addResult('Tests workspace view file system headers\n');
-  await TestRunner.loadTestModule('bindings_test_runner');
   const fs = new BindingsTestRunner.TestFileSystem('/this/is/a/test');
   await fs.reportCreatedPromise();
 
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index 9819b92..e109ddb 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -388,6 +388,7 @@
 widows: 2
 width: 100px
 will-change: auto
+word-boundary-detection: normal
 word-break: normal
 word-spacing: 0px
 writing-mode: horizontal-tb
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index 464d957..f7ae48f0 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -642,6 +642,7 @@
 widows
 width
 willChange
+wordBoundaryDetection
 wordBreak
 wordSpacing
 wordWrap
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index acedb9c..aa32e46f 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -425,6 +425,7 @@
     widows
     width
     will-change
+    word-boundary-detection
     word-break
     word-spacing
     writing-mode
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-text/parsing/word-boundary-detection-auto.html b/third_party/blink/web_tests/wpt_internal/css/css-text/parsing/word-boundary-detection-auto.html
new file mode 100644
index 0000000..b3f40621
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-text/parsing/word-boundary-detection-auto.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Text Module Test: parsing word-boundary-detection with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-text-4/#word-boundary-detection">
+<meta name="assert" content="word-boundary-detection supports the full grammar 'normal | manual | auto(<lang>) '.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+/*
+ * There are valid/invalid/computed tests in `wpt/css/css-text/parsing`, but
+ * they can't test the `auto(lang)` values because whether it's valid or not
+ * depends on supported languages, which is UA dependent. See the comments in
+ * these tests.
+ *
+ * These tests complement the `auto(lang)` part.
+ */
+test_valid_value("word-boundary-detection", "auto(ja)");
+
+test_invalid_value("word-boundary-detection", "auto(en)");
+test_invalid_value("word-boundary-detection", "auto(zh)");
+
+test_computed_value("word-boundary-detection", "auto(ja)");
+</script>
+</body>
+</html>
diff --git a/tools/clang/scripts/upload_revision.py b/tools/clang/scripts/upload_revision.py
index aae8bb7..93677c8e 100755
--- a/tools/clang/scripts/upload_revision.py
+++ b/tools/clang/scripts/upload_revision.py
@@ -10,11 +10,9 @@
 from __future__ import print_function
 
 import argparse
-import fnmatch
 import itertools
 import os
 import re
-import shutil
 import subprocess
 import sys
 import urllib.request
@@ -28,7 +26,7 @@
     os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..',
                  'rust'))
 
-from build_rust import RUST_GIT_URL, RUST_SRC_DIR
+from build_rust import (RUST_GIT_URL, RUST_SRC_DIR)
 
 # Path constants.
 THIS_DIR = os.path.dirname(__file__)
@@ -37,6 +35,10 @@
 RUST_UPDATE_PY_PATH = os.path.join(THIS_DIR, '..', '..', 'rust',
                                    'update_rust.py')
 BUILD_RUST_PY_PATH = os.path.join(THIS_DIR, '..', '..', 'rust', 'build_rust.py')
+GNRT_STDLIB_PY_PATH = os.path.join(THIS_DIR, '..', '..', 'rust',
+                                   'gnrt_stdlib.py')
+RUST_STDLIB_RULES_GN_PATH = os.path.join(CHROMIUM_DIR, 'build', 'rust', 'std',
+                                         'rules', 'BUILD.gn')
 
 # Constants for finding HEAD.
 CLANG_URL = 'https://api.github.com/repos/llvm/llvm-project/git/refs/heads/main'
@@ -297,7 +299,9 @@
     print('Cannot set both --skip-clang and --skip-rust.')
     sys.exit(1)
 
-  if not args.skip_clang:
+  if args.skip_clang:
+    clang_version = '-skipped-'
+  else:
     if args.clang_git_hash:
       clang_git_hash = args.clang_git_hash
     else:
@@ -309,10 +313,10 @@
     clang_version = ClangVersion(GetCommitDescription(clang_git_hash),
                                  args.clang_sub_revision)
     os.chdir(CHROMIUM_DIR)
-  else:
-    clang_version = '-skipped-'
 
-  if not args.skip_rust:
+  if args.skip_rust:
+    rust_version = '-skipped-'
+  else:
     if args.rust_git_hash:
       rust_git_hash = args.rust_git_hash
     else:
@@ -320,8 +324,9 @@
     CheckoutGitRepo("Rust", RUST_GIT_URL, rust_git_hash, RUST_SRC_DIR)
     rust_version = RustVersion(rust_git_hash, args.rust_sub_revision)
     os.chdir(CHROMIUM_DIR)
-  else:
-    rust_version = '-skipped-'
+
+    # Build and run gnrt to update the stdlib GN rules.
+    RunCommand([GNRT_STDLIB_PY_PATH, '--rust-src-dir', RUST_SRC_DIR])
 
   print(f'Making a patch for Clang {clang_version} and Rust {rust_version}')
 
@@ -378,6 +383,7 @@
   Git('add',
       CLANG_UPDATE_PY_PATH,
       RUST_UPDATE_PY_PATH,
+      RUST_STDLIB_RULES_GN_PATH,
       no_run=args.no_git)
   Git('commit', '-m', commit_message, no_run=args.no_git)
   Git('cl', 'upload', '-f', '--bypass-hooks', no_run=args.no_git)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5c04d3a0..f8e2fdff 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -39681,7 +39681,7 @@
   <int value="1760" label="BaseWithDataHref"/>
   <int value="1761" label="BaseWithNewlinesInTarget"/>
   <int value="1762" label="BaseWithOpenBracketInTarget"/>
-  <int value="1763" label="BaseWouldBeBlockedByDefaultSrc"/>
+  <int value="1763" label="OBSOLETE_BaseWouldBeBlockedByDefaultSrc"/>
   <int value="1764" label="V8AssigmentExpressionLHSIsCallInSloppy"/>
   <int value="1765" label="V8AssigmentExpressionLHSIsCallInStrict"/>
   <int value="1766" label="V8PromiseConstructorReturnedUndefined"/>
@@ -83740,6 +83740,15 @@
   <int value="3" label="Popup clicked through (abusive)"/>
 </enum>
 
+<enum name="PopupProvider">
+<!--
+      Privacy Team wants to monitor additions to this enum.
+-->
+
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Google"/>
+</enum>
+
 <enum name="PopupSafeBrowsingStatus">
   <int value="0" label="Popup has no safe-browsing status"/>
   <int value="1"
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 696d700..699b6f8 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -15388,6 +15388,20 @@
   <summary>
     Recorded when the user first interacts in a pop-up with opener access.
   </summary>
+  <metric name="OpenerHasSameSiteIframe" enum="HasSameSiteIframe">
+    <summary>
+      &quot;True&quot; iff the opener page contains an iframe whose URL belongs
+      to the same registrable domain as this pop-up. &quot;Unknown&quot; if the
+      opener page is closed or the user navigates its frame to another page
+      before interacting with this pop-up.
+    </summary>
+  </metric>
+  <metric name="PopupId">
+    <summary>
+      A random integer to identify this pop-up and associate this event with
+      other OpenerHeuristic.* events relating to the same pop-up.
+    </summary>
+  </metric>
   <metric name="SecondsSinceCommitted">
     <summary>
       The number of seconds since the initial pop-up navigation committed, until
@@ -15418,6 +15432,20 @@
       the nearest exponential bucket (out of 50 buckets).
     </summary>
   </metric>
+  <metric name="OpenerHasSameSiteIframe" enum="HasSameSiteIframe">
+    <summary>
+      &quot;True&quot; iff the opener page contains an iframe whose URL belongs
+      to the same registrable domain as this pop-up. &quot;Unknown&quot; if the
+      opener page is closed or the user navigates its frame to another page
+      before interacting with this pop-up.
+    </summary>
+  </metric>
+  <metric name="PopupId">
+    <summary>
+      A random integer to identify this pop-up and associate this event with
+      other OpenerHeuristic.* events relating to the same pop-up.
+    </summary>
+  </metric>
 </event>
 
 <event name="OpenerHeuristic.TopLevel">
@@ -15430,11 +15458,24 @@
   <metric name="HasSameSiteIframe" enum="HasSameSiteIframe">
     <summary>
       &quot;True&quot; iff the top-level page contains an iframe whose URL
-      belongs to the same site (eTLD+1) as the pop-up. &quot;Unknown&quot; if
-      the top-level page is closed or the user navigates the main frame to
+      belongs to the same registrable domain as the pop-up. &quot;Unknown&quot;
+      if the top-level page is closed or the user navigates the main frame to
       another page before interacting with the pop-up.
     </summary>
   </metric>
+  <metric name="PopupId">
+    <summary>
+      A random integer to identify the pop-up and associate this event with
+      other OpenerHeuristic.* events relating to the same pop-up.
+    </summary>
+  </metric>
+  <metric name="PopupProvider" enum="PopupProvider">
+    <summary>
+      An enum identifying the JavaScript framework used to open the pop-up, when
+      it can be recognized. Typically an SSO library such as Google Sign-In.
+      &quot;Unknown&quot; when no framework is recognized.
+    </summary>
+  </metric>
 </event>
 
 <event name="OptimizationGuide" singular="True">
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 8c0cd90..fc6e882 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -8,8 +8,8 @@
 UNSCHEDULED_blink_perf.service_worker,"yyanagisawa@chromium.org, chrome-worker@google.com",Blink>ServiceWorker,https://bit.ly/blink-perf-benchmarks,
 UNSCHEDULED_blink_perf.view_transitions,"bokan@chromium.org, khushalsagar@chromium.org, vmpstr@chromium.org",Blink>ViewTransitions,https://bit.ly/blink-perf-benchmarks,all
 UNSCHEDULED_dummy_wpr_benchmark.loading_using_wpr,maxqli@googl.com,Test>Telemetry,,
-UNSCHEDULED_jetstream20,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream/in-depth.html,all
-UNSCHEDULED_jetstream21,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream/in-depth.html,all
+UNSCHEDULED_jetstream20,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream2.0/in-depth.html,all
+UNSCHEDULED_jetstream21,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream2.1/in-depth.html,all
 UNSCHEDULED_loading.mbi,blink-isolation-dev@chromium.org,Blink>Internals>Modularization,https://bit.ly/loading-benchmarks,many_agents
 UNSCHEDULED_speedometer1.0,"cbruni@chromium.org, vahl@chromium.org",Blink>JavaScript,https://browserbench.org/Speedometer,all
 UNSCHEDULED_speedometer2.0,"cbruni@chromium.org, vahl@chromium.org",Blink>JavaScript,https://browserbench.org/Speedometer2.0,all
@@ -44,8 +44,8 @@
 desktop_ui,"yuhengh@chromium.org, tluk@chromium.org, romanarora@chromium.org",UI>Browser,https://chromium.googlesource.com/chromium/src/+/main/docs/speed/benchmark/harnesses/desktop_ui.md,smoke_test
 dummy_benchmark.noisy_benchmark_1,"johnchen@chromium.org, wenbinzhang@google.com",Test>Telemetry,,
 dummy_benchmark.stable_benchmark_1,"johnchen@chromium.org, wenbinzhang@google.com",Test>Telemetry,,
-jetstream2,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream/in-depth.html,all
-jetstream2-minormc,omerkatz@chromium.org,Blink>JavaScript>GarbageCollection,https://browserbench.org/JetStream/in-depth.html,all
+jetstream2,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream2.0/in-depth.html,all
+jetstream2-minormc,omerkatz@chromium.org,Blink>JavaScript>GarbageCollection,https://browserbench.org/JetStream2.0/in-depth.html,all
 load_library_perf_tests,"xhwang@chromium.org, jrummell@chromium.org",Internals>Media>Encrypted,,
 loading.desktop,"kouhei@chromium.org, ksakamoto@chromium.org",Blink>Loader,https://bit.ly/loading-benchmarks,"abridged,cache_temperature_cold,cache_temperature_warm,international,intl_ar_fa_he,intl_es_fr_pt_BR,intl_hi_ru,intl_ja_zh,intl_ko_th_vi,typical"
 loading.mobile,"kouhei@chromium.org, ksakamoto@chromium.org",Blink>Loader,https://bit.ly/loading-benchmarks,"abridged,cache_temperature_cold,cache_temperature_hot,cache_temperature_warm,easy_ttfmp,easy_tti,global,pwa,tough_ttfmp,tough_tti"
diff --git a/tools/perf/benchmarks/jetstream2.py b/tools/perf/benchmarks/jetstream2.py
index e250e96..1d59e15 100644
--- a/tools/perf/benchmarks/jetstream2.py
+++ b/tools/perf/benchmarks/jetstream2.py
@@ -39,9 +39,10 @@
                       help="Only run specific tests, separated by commas.")
 
 
-@benchmark.Info(emails=['hablich@chromium.org', 'tcwang@chromium.org'],
-                component='Blink>JavaScript',
-                documentation_url='https://browserbench.org/JetStream/in-depth.html')
+@benchmark.Info(
+    emails=['hablich@chromium.org', 'tcwang@chromium.org'],
+    component='Blink>JavaScript',
+    documentation_url='https://browserbench.org/JetStream2.0/in-depth.html')
 class JetStream20(_JetStream2Base):
   """JetStream 2.0"""
   @classmethod
@@ -55,7 +56,7 @@
 @benchmark.Info(
     emails=['hablich@chromium.org', 'tcwang@chromium.org'],
     component='Blink>JavaScript',
-    documentation_url='https://browserbench.org/JetStream/in-depth.html')
+    documentation_url='https://browserbench.org/JetStream2.1/in-depth.html')
 class JetStream21(_JetStream2Base):
   """JetStream 2.1"""
   @classmethod
@@ -69,8 +70,8 @@
 @benchmark.Info(
     emails=['hablich@chromium.org', 'tcwang@chromium.org'],
     component='Blink>JavaScript',
-    documentation_url='https://browserbench.org/JetStream/in-depth.html')
-class JetStream2(JetStream20):
+    documentation_url='https://browserbench.org/JetStream2.0/in-depth.html')
+class JetStream2(_JetStream2Base):
   """Latest JetStream2 """
   @classmethod
   def Name(cls):
@@ -83,8 +84,8 @@
 @benchmark.Info(
     emails=['omerkatz@chromium.org'],
     component='Blink>JavaScript>GarbageCollection',
-    documentation_url='https://browserbench.org/JetStream/in-depth.html')
-class JetStream2MinorMC(JetStream20):
+    documentation_url='https://browserbench.org/JetStream2.0/in-depth.html')
+class JetStream2MinorMC(JetStream2):
   """Latest JetStream2 with the MinorMC flag.
 
   Shows the performance of upcoming MinorMC young generation GC in V8.
@@ -93,8 +94,5 @@
   def Name(cls):
     return 'jetstream2-minormc'
 
-  def CreateStorySet(self, options):
-    return page_sets.JetStream2StorySet(options.test_list)
-
   def SetExtraBrowserOptions(self, options):
     options.AppendExtraBrowserArgs('--js-flags=--minor-mc')
diff --git a/tools/perf/page_sets/data/jetstream2.json b/tools/perf/page_sets/data/jetstream2.json
index 455accd..5788888 100644
--- a/tools/perf/page_sets/data/jetstream2.json
+++ b/tools/perf/page_sets/data/jetstream2.json
@@ -4,10 +4,10 @@
             "DEFAULT": "jetstream2_3c4406a678.wprgo"
         },
         "JetStream20": {
-            "DEFAULT": "jetstream2_3c4406a678.wprgo"
+            "DEFAULT": "jetstream2_22f8a5ea22.wprgo"
         },
         "JetStream21": {
-            "DEFAULT": "jetstream2_3c4406a678.wprgo"
+            "DEFAULT": "jetstream2_8725e67100.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/jetstream2_22f8a5ea22.wprgo.sha1 b/tools/perf/page_sets/data/jetstream2_22f8a5ea22.wprgo.sha1
new file mode 100644
index 0000000..2d7c40b77
--- /dev/null
+++ b/tools/perf/page_sets/data/jetstream2_22f8a5ea22.wprgo.sha1
@@ -0,0 +1 @@
+22f8a5ea22d05a331c84b2ab7822f0abfa772f9c
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/jetstream2_8725e67100.wprgo.sha1 b/tools/perf/page_sets/data/jetstream2_8725e67100.wprgo.sha1
new file mode 100644
index 0000000..363d8029
--- /dev/null
+++ b/tools/perf/page_sets/data/jetstream2_8725e67100.wprgo.sha1
@@ -0,0 +1 @@
+8725e67100af1e7d47cb590e0be8fc83a68a4a6a
\ No newline at end of file
diff --git a/tools/perf/page_sets/jetstream2_pages.py b/tools/perf/page_sets/jetstream2_pages.py
index 3d44e7b3..b9486dc 100644
--- a/tools/perf/page_sets/jetstream2_pages.py
+++ b/tools/perf/page_sets/jetstream2_pages.py
@@ -7,7 +7,7 @@
 
 
 class _JetStream2Story(press_story.PressStory):
-  URL = 'http://browserbench.org/JetStream/'
+  URL = 'http://browserbench.org/JetStream2.1/'
 
   def __init__(self, page_set, test_list):
     super(_JetStream2Story, self).__init__(page_set)
@@ -78,13 +78,16 @@
 
 class JetStream20Story(_JetStream2Story):
   NAME = 'JetStream20'
+  URL = 'http://browserbench.org/JetStream2.0/'
 
 
 class JetStream21Story(_JetStream2Story):
   NAME = 'JetStream21'
+  URL = 'http://browserbench.org/JetStream2.1/'
 
 
 class JetStream2Story(JetStream20Story):
+  URL = 'http://browserbench.org/JetStream/'
   NAME = 'JetStream2'
 
 
@@ -105,5 +108,5 @@
   _STORY_CLS = JetStream21Story
 
 
-class JetStream2StorySet(JetStream20StorySet):
+class JetStream2StorySet(_JetStream2StorySet):
   _STORY_CLS = JetStream2Story
diff --git a/tools/rust/build_bindgen.py b/tools/rust/build_bindgen.py
index 7545850..05b6cd3 100755
--- a/tools/rust/build_bindgen.py
+++ b/tools/rust/build_bindgen.py
@@ -55,13 +55,13 @@
 def InstallRustBetaSysroot(rust_git_hash, target_triples):
     if os.path.exists(RUST_BETA_SYSROOT_DIR):
         RmTree(RUST_BETA_SYSROOT_DIR)
-    InstallBetaPackage(FetchBetaPackage('cargo', RUST_REVISION),
+    InstallBetaPackage(FetchBetaPackage('cargo', rust_git_hash),
                        RUST_BETA_SYSROOT_DIR)
-    InstallBetaPackage(FetchBetaPackage('rustc', RUST_REVISION),
+    InstallBetaPackage(FetchBetaPackage('rustc', rust_git_hash),
                        RUST_BETA_SYSROOT_DIR)
     for t in target_triples:
         InstallBetaPackage(
-            FetchBetaPackage('rust-std', RUST_REVISION, triple=t),
+            FetchBetaPackage('rust-std', rust_git_hash, triple=t),
             RUST_BETA_SYSROOT_DIR)
     return RUST_BETA_SYSROOT_DIR
 
diff --git a/tools/rust/build_rust.py b/tools/rust/build_rust.py
index 725d73147..f158694 100755
--- a/tools/rust/build_rust.py
+++ b/tools/rust/build_rust.py
@@ -60,7 +60,7 @@
                     GetDefaultHostOs, RmTree, UpdatePackage)
 
 from update_rust import (RUST_REVISION, RUST_TOOLCHAIN_OUT_DIR,
-                         STAGE0_JSON_SHA256, THIRD_PARTY_DIR, THIS_DIR,
+                         STAGE0_JSON_SHA256, THIRD_PARTY_DIR,
                          VERSION_STAMP_PATH, GetLatestRevision)
 
 EXCLUDED_TESTS = [
@@ -89,7 +89,7 @@
     os.path.join('tests', 'codegen', 'vec-shrink-panik.rs'),
 ]
 
-CLANG_SCRIPTS_DIR = os.path.join(THIS_DIR, '..', 'clang', 'scripts')
+CLANG_SCRIPTS_DIR = os.path.join(CHROMIUM_DIR, 'tools', 'clang', 'scripts')
 
 RUST_GIT_URL = ('https://chromium.googlesource.com/external/' +
                 'github.com/rust-lang/rust')
diff --git a/tools/rust/gnrt_stdlib.py b/tools/rust/gnrt_stdlib.py
new file mode 100755
index 0000000..58f6593
--- /dev/null
+++ b/tools/rust/gnrt_stdlib.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# 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.
+'''Builds the gnrt tool and runs it to generate stdlib GN rules.
+'''
+
+import argparse
+import os
+import sys
+import tempfile
+
+# Get variables and helpers from Clang update script.
+sys.path.append(
+    os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'clang',
+                 'scripts'))
+from build import (RunCommand)
+from update import (CHROMIUM_DIR)
+
+from build_bindgen import (EXE, InstallRustBetaSysroot)
+from build_rust import (RustTargetTriple)
+from update_rust import (RUST_REVISION)
+
+BUILD_RUST_PY_PATH = os.path.join(CHROMIUM_DIR, 'tools', 'rust',
+                                  'build_rust.py')
+GNRT_CARGO_TOML_PATH = os.path.join(CHROMIUM_DIR, 'tools', 'crates', 'gnrt',
+                                    'Cargo.toml')
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description='build and run gnrt for stdlib')
+    parser.add_argument('--rust-src-dir',
+                        type=str,
+                        required=True,
+                        metavar='RUST_SRC_DIR',
+                        help='path to the root of the Rust src tree')
+    args = parser.parse_args()
+
+    # This step gets the dependencies in the Rust git checkout.
+    RunCommand([BUILD_RUST_PY_PATH, '--update-deps'])
+
+    # Get a Rust sysroot to build gnrt with.
+    root = InstallRustBetaSysroot(RUST_REVISION, [RustTargetTriple(None)])
+    cargo_bin = os.path.join(root, 'bin', f'cargo{EXE}')
+    rustc_bin = os.path.join(root, 'bin', f'rustc{EXE}')
+
+    # Build and run gnrt to update the stdlib GN rules.
+    with tempfile.TemporaryDirectory() as cargo_work_dir:
+        cargo_env = os.environ
+        cargo_env['CARGO_HOME'] = cargo_work_dir
+        RunCommand([
+            cargo_bin, '--quiet', '--locked', 'build', '--release',
+            '--manifest-path', GNRT_CARGO_TOML_PATH,
+            f'--target-dir={cargo_work_dir}', '--config',
+            f'build.rustc="{rustc_bin}"'
+        ])
+        RunCommand([
+            os.path.join(cargo_work_dir, 'release', f'gnrt{EXE}'), 'gen',
+            f'--for-std={os.path.relpath(args.rust_src_dir, CHROMIUM_DIR)}'
+        ])
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 0954a0b..6aa9eb6 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -374,7 +374,7 @@
  <item id="managed_acccount_signin_restrictions_secure_connect_chromeos" added_in_milestone="109" content_hash_code="05958d4c" os_list="chromeos" file_path="chrome/browser/ui/webui/signin/ash/user_cloud_signin_restriction_policy_fetcher.cc" />
  <item id="ntp_custom_background" added_in_milestone="109" content_hash_code="08080082" os_list="linux,windows,chromeos" file_path="chrome/browser/search/background/ntp_custom_background_service.cc" />
  <item id="iwa_policy_update_manifest" added_in_milestone="110" second_id="iwa_update_manifest_fetcher" content_hash_code="07d65fe9" os_list="chromeos" file_path="chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc" />
- <item id="iwa_policy_signed_web_bundle" added_in_milestone="110" content_hash_code="03a6049c" os_list="chromeos" file_path="chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc" />
+ <item id="iwa_policy_signed_web_bundle" added_in_milestone="110" second_id="iwa_bundle_downloader" content_hash_code="00a27db9" os_list="chromeos" file_path="chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc" />
  <item id="quick_start_challenge_bytes_fetcher" added_in_milestone="110" content_hash_code="050ba600" os_list="chromeos" file_path="chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc" />
  <item id="customization_document" added_in_milestone="110" content_hash_code="007a3dda" os_list="chromeos" file_path="chrome/browser/ash/customization/customization_document.cc" />
  <item id="nearby_webrtc_connection" added_in_milestone="110" content_hash_code="070e1f72" os_list="chromeos" file_path="chrome/services/sharing/nearby/platform/webrtc.cc" />
@@ -420,4 +420,5 @@
  <item id="glanceables_classroom_integration" added_in_milestone="115" content_hash_code="05bfd7c6" os_list="chromeos" file_path="chrome/browser/ui/ash/glanceables/glanceables_classroom_client_impl.cc" />
  <item id="promise_app_service_download_icon" added_in_milestone="115" content_hash_code="00521032" os_list="chromeos" file_path="chrome/browser/apps/app_service/promise_apps/promise_app_service.cc" />
  <item id="iwa_update_manifest_fetcher" added_in_milestone="116" type="completing" content_hash_code="06867096" os_list="chromeos" semantics_fields="4,5,7,8,9" policy_fields="-1" file_path="chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.cc" />
+ <item id="iwa_bundle_downloader" added_in_milestone="116" type="completing" content_hash_code="04d2beae" os_list="chromeos" semantics_fields="4,5,7,8,9" policy_fields="-1" file_path="chrome/browser/web_applications/isolated_web_apps/isolated_web_app_downloader.cc" />
 </annotations>
diff --git a/ui/events/blink/blink_event_util.cc b/ui/events/blink/blink_event_util.cc
index e7f0d58..1d030d1 100644
--- a/ui/events/blink/blink_event_util.cc
+++ b/ui/events/blink/blink_event_util.cc
@@ -454,6 +454,7 @@
       break;
     case ET_GESTURE_TAP_DOWN:
       gesture.SetType(WebInputEvent::Type::kGestureTapDown);
+      gesture.data.tap_down.tap_down_count = details.tap_down_count();
       gesture.data.tap_down.width =
           IfNanUseMaxFloat(details.bounding_box_f().width());
       gesture.data.tap_down.height =
diff --git a/ui/touch_selection/longpress_drag_selector.cc b/ui/touch_selection/longpress_drag_selector.cc
index 4090070..42f6181 100644
--- a/ui/touch_selection/longpress_drag_selector.cc
+++ b/ui/touch_selection/longpress_drag_selector.cc
@@ -32,7 +32,7 @@
       touch_down_position_.SetPoint(event.GetX(), event.GetY());
       touch_down_time_ = event.GetEventTime();
       has_longpress_drag_start_anchor_ = false;
-      SetState(LONGPRESS_PENDING);
+      SetState(INITIATING_GESTURE_PENDING);
       return false;
 
     case MotionEvent::Action::UP:
@@ -117,7 +117,7 @@
   // observed touch stream. We only know that the gesture sequence is downstream
   // from the touch sequence. Using a time/distance heuristic helps ensure that
   // the observed longpress corresponds to the active touch sequence.
-  if (state_ == LONGPRESS_PENDING &&
+  if (state_ == INITIATING_GESTURE_PENDING &&
       // Ensure the down event occurs *before* the longpress event. Use a
       // small time epsilon to account for floating point time conversion.
       (touch_down_time_ < event_time + base::Microseconds(10)) &&
@@ -126,6 +126,20 @@
   }
 }
 
+void LongPressDragSelector::OnDoublePressEvent(base::TimeTicks event_time,
+                                               const gfx::PointF& position) {
+  // Handle a double press the same way as a long press.
+  if (state_ == INITIATING_GESTURE_PENDING &&
+      // Check event time and position to ensure that the observed double
+      // press corresponds to the active touch sequence. It should be ok to
+      // check the exact times and positions here, since a tap down gesture
+      // event is created directly from the corresponding down motion event when
+      // the gesture is initially detected.
+      touch_down_time_ == event_time && touch_down_position_ == position) {
+    SetState(SELECTION_PENDING);
+  }
+}
+
 void LongPressDragSelector::OnScrollBeginEvent() {
   SetState(INACTIVE);
 }
diff --git a/ui/touch_selection/longpress_drag_selector.h b/ui/touch_selection/longpress_drag_selector.h
index eac8208..3c9cb3a 100644
--- a/ui/touch_selection/longpress_drag_selector.h
+++ b/ui/touch_selection/longpress_drag_selector.h
@@ -25,8 +25,8 @@
   virtual gfx::PointF GetSelectionEnd() const = 0;
 };
 
-// Supports text selection via touch dragging after a longpress-initiated
-// selection.
+// Supports text selection via touch dragging after a longpress- or
+// doublepress-initiated selection.
 class UI_TOUCH_SELECTION_EXPORT LongPressDragSelector
     : public TouchSelectionDraggable {
  public:
@@ -41,6 +41,10 @@
   void OnLongPressEvent(base::TimeTicks event_time,
                         const gfx::PointF& position);
 
+  // Called just prior to a double press event being handled.
+  void OnDoublePressEvent(base::TimeTicks event_time,
+                          const gfx::PointF& position);
+
   // Called when a scroll is going to happen to cancel longpress-drag gesture.
   void OnScrollBeginEvent();
 
@@ -51,7 +55,7 @@
  private:
   enum SelectionState {
     INACTIVE,
-    LONGPRESS_PENDING,
+    INITIATING_GESTURE_PENDING,
     SELECTION_PENDING,
     DRAG_PENDING,
     DRAGGING
diff --git a/ui/touch_selection/longpress_drag_selector_unittest.cc b/ui/touch_selection/longpress_drag_selector_unittest.cc
index ebdeaca1..11b0426 100644
--- a/ui/touch_selection/longpress_drag_selector_unittest.cc
+++ b/ui/touch_selection/longpress_drag_selector_unittest.cc
@@ -122,6 +122,53 @@
   EXPECT_TRUE(GetAndResetActiveStateChanged());
 }
 
+TEST_F(LongPressDragSelectorTest, DoublePressDrag) {
+  LongPressDragSelector selector(this);
+  MockMotionEvent event;
+
+  // Start a touch sequence.
+  EXPECT_FALSE(selector.WillHandleTouchEvent(event.PressPoint(0, 0)));
+  EXPECT_FALSE(GetAndResetActiveStateChanged());
+
+  // Activate a double press triggered selection.
+  constexpr gfx::PointF selection_start(0, 10);
+  constexpr gfx::PointF selection_end(10, 10);
+  selector.OnDoublePressEvent(event.GetEventTime(), gfx::PointF());
+  EXPECT_FALSE(GetAndResetActiveStateChanged());
+
+  // Motion should not be consumed until a selection is detected.
+  EXPECT_FALSE(selector.WillHandleTouchEvent(event.MovePoint(0, 0, 0)));
+  SetSelection(selection_start, selection_end);
+  selector.OnSelectionActivated();
+  EXPECT_TRUE(GetAndResetActiveStateChanged());
+  EXPECT_FALSE(IsDragging());
+
+  // Initiate drag motion.  Note that the first move event after activation is
+  // used to initialize the drag start anchor.
+  EXPECT_TRUE(selector.WillHandleTouchEvent(event.MovePoint(0, 0, 0)));
+  EXPECT_FALSE(IsDragging());
+
+  // The first slop exceeding motion will start the drag. As the motion is
+  // downward, the end selection point should be moved.
+  EXPECT_TRUE(selector.WillHandleTouchEvent(event.MovePoint(0, 0, kSlop)));
+  EXPECT_TRUE(IsDragging());
+  EXPECT_EQ(selection_end, DragPosition());
+
+  // Subsequent motion will extend the selection.
+  EXPECT_TRUE(selector.WillHandleTouchEvent(event.MovePoint(0, 0, kSlop * 2)));
+  EXPECT_TRUE(IsDragging());
+  EXPECT_EQ(selection_end + gfx::Vector2dF(0, kSlop), DragPosition());
+  EXPECT_TRUE(selector.WillHandleTouchEvent(event.MovePoint(0, 0, kSlop * 3)));
+  EXPECT_TRUE(IsDragging());
+  EXPECT_EQ(selection_end + gfx::Vector2dF(0, kSlop * 2), DragPosition());
+
+  // Release the touch sequence, ending the drag. The selector will never
+  // consume the start/end events, only move events after a double press.
+  EXPECT_FALSE(selector.WillHandleTouchEvent(event.ReleasePoint()));
+  EXPECT_FALSE(IsDragging());
+  EXPECT_TRUE(GetAndResetActiveStateChanged());
+}
+
 TEST_F(LongPressDragSelectorTest, BasicReverseDrag) {
   LongPressDragSelector selector(this);
   MockMotionEvent event;
diff --git a/ui/touch_selection/touch_selection_controller.cc b/ui/touch_selection/touch_selection_controller.cc
index 9940afc..da8c0404 100644
--- a/ui/touch_selection/touch_selection_controller.cc
+++ b/ui/touch_selection/touch_selection_controller.cc
@@ -200,6 +200,13 @@
   response_pending_input_event_ = LONG_PRESS;
 }
 
+void TouchSelectionController::HandleDoublePressEvent(
+    base::TimeTicks event_time,
+    const gfx::PointF& location) {
+  longpress_drag_selector_.OnDoublePressEvent(event_time, location);
+  response_pending_input_event_ = LONG_PRESS;
+}
+
 void TouchSelectionController::OnScrollBeginEvent() {
   // When there is an active selection, if the user performs a long-press that
   // does not trigger a new selection (e.g. a long-press on an empty area) and
diff --git a/ui/touch_selection/touch_selection_controller.h b/ui/touch_selection/touch_selection_controller.h
index 05f2c483..c129bcf 100644
--- a/ui/touch_selection/touch_selection_controller.h
+++ b/ui/touch_selection/touch_selection_controller.h
@@ -103,6 +103,10 @@
   void HandleLongPressEvent(base::TimeTicks event_time,
                                 const gfx::PointF& location);
 
+  // To be called before forwarding a double press event.
+  void HandleDoublePressEvent(base::TimeTicks event_time,
+                              const gfx::PointF& location);
+
   // To be called before forwarding a gesture scroll begin event to prevent
   // long-press drag.
   void OnScrollBeginEvent();
diff --git a/ui/touch_selection/touch_selection_controller_unittest.cc b/ui/touch_selection/touch_selection_controller_unittest.cc
index b89fea19..77ae95f 100644
--- a/ui/touch_selection/touch_selection_controller_unittest.cc
+++ b/ui/touch_selection/touch_selection_controller_unittest.cc
@@ -176,6 +176,10 @@
                                           kIgnoredPoint);
   }
 
+  void OnDoublePressEvent() {
+    controller().HandleDoublePressEvent(base::TimeTicks(), kIgnoredPoint);
+  }
+
   void OnTapEvent() {
     controller().HandleTapEvent(kIgnoredPoint, 1);
   }
@@ -1034,6 +1038,76 @@
   EXPECT_TRUE(test_controller.GetEndVisible());
 }
 
+TEST_F(TouchSelectionControllerTest, DoublePressDrag) {
+  TouchSelectionController::Config config = kDefaultConfig;
+  config.enable_longpress_drag_selection = true;
+  InitializeControllerWithConfig(config);
+  TouchSelectionControllerTestApi test_controller(&controller());
+
+  // Start a touch sequence.
+  MockMotionEvent event;
+  EXPECT_FALSE(controller().WillHandleTouchEvent(event.PressPoint(0, 0)));
+
+  // Activate a double press triggered selection.
+  constexpr gfx::RectF start_rect(-50, 0, 0, 10);
+  constexpr gfx::RectF end_rect(50, 0, 0, 10);
+  constexpr bool visible = true;
+  OnDoublePressEvent();
+  ChangeSelection(start_rect, visible, end_rect, visible);
+  EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLES_SHOWN));
+  EXPECT_EQ(start_rect.bottom_left(), GetLastEventStart());
+
+  // The handles should remain invisible while the touch release and double
+  // press drag gesture are pending.
+  EXPECT_FALSE(test_controller.GetStartVisible());
+  EXPECT_FALSE(test_controller.GetEndVisible());
+  EXPECT_EQ(0.f, test_controller.GetStartAlpha());
+  EXPECT_EQ(0.f, test_controller.GetEndAlpha());
+
+  // Start dragging.
+  EXPECT_TRUE(controller().WillHandleTouchEvent(event.MovePoint(0, 0, 0)));
+  EXPECT_THAT(GetAndResetEvents(), IsEmpty());
+
+  EXPECT_TRUE(controller().WillHandleTouchEvent(
+      event.MovePoint(0, 0, kDefaultTapSlop)));
+  EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STARTED));
+  EXPECT_EQ(start_rect.CenterPoint(), GetLastSelectionStart());
+  EXPECT_EQ(end_rect.CenterPoint(), GetLastSelectionEnd());
+
+  // Movement after the start of drag will be relative to the moved endpoint.
+  EXPECT_TRUE(controller().WillHandleTouchEvent(
+      event.MovePoint(0, 0, 2 * kDefaultTapSlop)));
+  EXPECT_TRUE(GetAndResetSelectionMoved());
+  EXPECT_EQ(end_rect.CenterPoint() + gfx::Vector2dF(0, kDefaultTapSlop),
+            GetLastSelectionEnd());
+
+  EXPECT_TRUE(controller().WillHandleTouchEvent(
+      event.MovePoint(0, kDefaultTapSlop, 2 * kDefaultTapSlop)));
+  EXPECT_TRUE(GetAndResetSelectionMoved());
+  EXPECT_EQ(
+      end_rect.CenterPoint() + gfx::Vector2dF(kDefaultTapSlop, kDefaultTapSlop),
+      GetLastSelectionEnd());
+
+  EXPECT_TRUE(controller().WillHandleTouchEvent(
+      event.MovePoint(0, 2 * kDefaultTapSlop, 2 * kDefaultTapSlop)));
+  EXPECT_TRUE(GetAndResetSelectionMoved());
+  EXPECT_EQ(end_rect.CenterPoint() +
+                gfx::Vector2dF(2 * kDefaultTapSlop, kDefaultTapSlop),
+            GetLastSelectionEnd());
+
+  // The handles should still be hidden.
+  EXPECT_FALSE(test_controller.GetStartVisible());
+  EXPECT_FALSE(test_controller.GetEndVisible());
+  EXPECT_EQ(0.f, test_controller.GetStartAlpha());
+  EXPECT_EQ(0.f, test_controller.GetEndAlpha());
+
+  // Releasing the touch sequence should end the drag and show the handles.
+  EXPECT_FALSE(controller().WillHandleTouchEvent(event.ReleasePoint()));
+  EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED));
+  EXPECT_TRUE(test_controller.GetStartVisible());
+  EXPECT_TRUE(test_controller.GetEndVisible());
+}
+
 TEST_F(TouchSelectionControllerTest, LongPressNoDrag) {
   TouchSelectionController::Config config = kDefaultConfig;
   config.enable_longpress_drag_selection = true;
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 5f51597..d8fd4fc 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -298,7 +298,8 @@
 };
 
 // Test visibility states triggered externally.
-TEST_F(NativeWidgetMacTest, HideAndShowExternally) {
+// TODO(crbug.com/1450876): Flaky.
+TEST_F(NativeWidgetMacTest, DISABLED_HideAndShowExternally) {
   Widget* widget = CreateTopLevelPlatformWidget();
   NSWindow* ns_window = widget->GetNativeWindow().GetNativeNSWindow();
   WidgetChangeObserver observer(widget);
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index bde6bc5a..8a7a96a 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1011,7 +1011,12 @@
 }
 
 void HWNDMessageHandler::ClearNativeFocus() {
-  ::SetFocus(hwnd());
+  // Headless windows don't get native focus, so just pretend we grabbed one.
+  if (IsHeadless()) {
+    delegate_->HandleNativeFocus(0);
+  } else {
+    ::SetFocus(hwnd());
+  }
 }
 
 void HWNDMessageHandler::SetCapture() {
@@ -1507,7 +1512,12 @@
   if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) &&
       !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
     // The window does not get keyboard messages unless we focus it.
-    SetFocus(hwnd());
+    // Headless windows don't get native focus, so just pretend we grabbed one.
+    if (IsHeadless()) {
+      delegate_->HandleNativeFocus(0);
+    } else {
+      ::SetFocus(hwnd());
+    }
   }
 }
 
@@ -2153,7 +2163,11 @@
 }
 
 void HWNDMessageHandler::OnKillFocus(HWND focused_window) {
-  delegate_->HandleNativeBlur(focused_window);
+  // Headless windows are believed to always have focus, so avoid
+  // reporting native focus changes.
+  if (!IsHeadless()) {
+    delegate_->HandleNativeBlur(focused_window);
+  }
   SetMsgHandled(FALSE);
 }
 
@@ -2741,7 +2755,11 @@
 }
 
 void HWNDMessageHandler::OnSetFocus(HWND last_focused_window) {
-  delegate_->HandleNativeFocus(last_focused_window);
+  // Headless windows are believed to always have focus, so avoid
+  // reporting native focus changes.
+  if (!IsHeadless()) {
+    delegate_->HandleNativeFocus(last_focused_window);
+  }
   SetMsgHandled(FALSE);
 }