diff --git a/BUILD.gn b/BUILD.gn
index 396d34c..56ca19a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1618,7 +1618,7 @@
 
 # Note: v8_target_cpu == arm allows using the V8 arm simulator on x86 for fuzzing.
 assert(
-    is_valid_x86_target || target_cpu != "x86" || v8_target_cpu != "arm",
+    is_valid_x86_target || target_cpu != "x86" || v8_target_cpu == "arm",
     "'target_cpu=x86' is not supported for 'target_os=$target_os'. Consider omitting 'target_cpu' (default) or using 'target_cpu=x64' instead.")
 
 group("chromium_builder_perf") {
diff --git a/DEPS b/DEPS
index b3baf81..99fc140 100644
--- a/DEPS
+++ b/DEPS
@@ -280,7 +280,7 @@
   # 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': '17c186a7b06bb9e726e51bd456a5a3375dd55e11',
+  'skia_revision': 'a10e6c1025274a5f2cc4958c39a102755b45bb34',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -288,7 +288,7 @@
   # 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': '32cb575032ee344d9e3bd5998c8756da3df8919a',
+  'angle_revision': '669d7b756e7958e62420be211be1175a35340519',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -307,7 +307,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:8.20220627.0.1',
+  'fuchsia_version': 'version:8.20220627.1.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -359,7 +359,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': '540decdfcca8b8ed83ff169d9012aa5de0559d5b',
+  'devtools_frontend_revision': 'd2741940ef1cf0bc2572e52e7e9677b74166416d',
   # 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.
@@ -395,7 +395,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': 'ceb7236547093db0a5d047ccb9987e4e87926195',
+  'dawn_revision': 'dadf11909a2efb8dea2d88261f6aa9715513d61c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -443,7 +443,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.
-  'libunwind_revision':    'b387062642f045b19adbf6a80757293caaf64e8b',
+  'libunwind_revision':    '49191c55bba0e64664954eec93a43d8eb11e5798',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -756,7 +756,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'acc2fc15095e1d879ae36869a6033eacab037476',
+    'url': Var('chromium_git') + '/website.git' + '@' + '8e7679573edc1c262d6a45b3c296f94404480275',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -780,7 +780,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '3a4ccf8e3c9a4422efe6b9af0c641e09b4afae2c',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '72287f8311a8a6afda482d451bfc9dbfb9f6c3f4',
       'condition': 'checkout_ios',
   },
 
@@ -850,7 +850,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'nG6AncubmrAP-eF2hN4DcUy2PBswQd1Q858Wmo45TmYC',
+          'version': 'b2KbbT2y6hWG7WA5VuK9xrMOmjzbiYsGe-66yhuTsJAC',
         },
       ],
       'dep_type': 'cipd',
@@ -861,7 +861,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'wiN7bcSq4QYkoOQAizwJYfjKHCcxfKD3EoqNN2tKAdgC',
+          'version': 'CimYFhjVHZdH5LoA1qL5PjKnqhNNEQcZWNCEZdIvh7sC',
         },
       ],
       'dep_type': 'cipd',
@@ -872,7 +872,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'gPqqbG-Feb8qAWJEQA9cMlMuv9Dy7IB9HHJukKKJIcwC',
+          'version': 'Vwry-Xk2N9ReN2pwZQycyXZYHFy4JisWdBmxgxSf7CUC',
         },
       ],
       'dep_type': 'cipd',
@@ -1208,7 +1208,7 @@
     Var('chromium_git') + '/external/github.com/google/gemmlowp.git' + '@' + '13d57703abca3005d97b19df1f2db731607a7dc2',
 
   'src/third_party/grpc/src': {
-      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + 'd1338d8751231bdc0d87e732d25420e87d24cffd',
+      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + '1be6e2c9ebfb4a26bebe6b3f3c45cffc70e71b68',
   },
 
   'src/third_party/freetype/src':
@@ -1553,7 +1553,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '588d85dc56ee732822f5ba6c09ca0bf43a7a7c24',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '2a59c7427cfd780bcb98dfad01772b44468edc7a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1723,7 +1723,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '21e01f20feaa79eb125c34e7da02444715a19078',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '70de463cfa367dc94a9072856e882481864c5415',
+    Var('webrtc_git') + '/src.git' + '@' + 'd58f5263844df2e24604c7fff7a932c6baf6ade3',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1796,7 +1796,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2b1b6191851c2a8a115334142f35c97d34b7176c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b30e642420249994535e6b8d9cc292b07d4034f7',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index aaf26bd..79ffdb3 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -57,6 +57,8 @@
     "aw_devtools_server.h",
     "aw_download_manager_delegate.cc",
     "aw_download_manager_delegate.h",
+    "aw_enterprise_authentication_app_link_manager.cc",
+    "aw_enterprise_authentication_app_link_manager.h",
     "aw_feature_entries.cc",
     "aw_feature_entries.h",
     "aw_feature_list.cc",
@@ -120,6 +122,8 @@
     "component_updater/trust_token_key_commitments_component_loader.h",
     "cookie_manager.cc",
     "cookie_manager.h",
+    "enterprise_authentication_app_link_policy_handler.cc",
+    "enterprise_authentication_app_link_policy_handler.h",
     "find_helper.cc",
     "find_helper.h",
     "icon_helper.cc",
@@ -224,6 +228,8 @@
     "//components/keyed_service/content",
     "//components/optimization_guide/core:bloomfilter",
     "//components/profile_metrics",
+    "//components/strings:components_strings_grit",
+    "//components/url_matcher",
 
     # Called via JNI in CrashpadMain
     "//components/crash/android:crashpad_main",
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index e2d0c5e4..ffc2e7f0 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -44,8 +44,10 @@
   "+components/services/heap_profiling",
   "+components/spellcheck/browser",
   "+components/spellcheck/common",
+  "+components/strings",
   "+components/tracing/common",
   "+components/url_formatter",
+  "+components/url_matcher",
   "+components/user_prefs",
   "+components/variations",
   "+components/visitedlink/browser",
diff --git a/android_webview/browser/aw_browser_policy_connector.cc b/android_webview/browser/aw_browser_policy_connector.cc
index 0880249a..cb32b7507 100644
--- a/android_webview/browser/aw_browser_policy_connector.cc
+++ b/android_webview/browser/aw_browser_policy_connector.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "android_webview/browser/aw_browser_process.h"
+#include "android_webview/browser/enterprise_authentication_app_link_policy_handler.h"
 #include "base/bind.h"
 #include "components/policy/core/browser/configuration_policy_handler_list.h"
 #include "components/policy/core/browser/url_blocklist_policy_handler.h"
@@ -50,16 +51,10 @@
       policy::key::kAuthAndroidNegotiateAccountType,
       prefs::kAuthAndroidNegotiateAccountType, base::Value::Type::STRING));
 
-  // TODO(ayushsha): Add custom SchemaValidation handler to
-  // * Validate the format of url.
-  // * Maximum authentication url that can be added.
   handlers->AddHandler(
-      std::make_unique<policy::SimpleSchemaValidatingPolicyHandler>(
+      std::make_unique<policy::EnterpriseAuthenticationAppLinkPolicyHandler>(
           policy::key::kEnterpriseAuthenticationAppLinkPolicy,
-          prefs::kEnterpriseAuthAppLinkPolicy, chrome_schema,
-          policy::SchemaOnErrorStrategy::SCHEMA_ALLOW_UNKNOWN,
-          policy::SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
-          policy::SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
+          prefs::kEnterpriseAuthAppLinkPolicy));
 
   return handlers;
 }
diff --git a/android_webview/browser/aw_browser_process.cc b/android_webview/browser/aw_browser_process.cc
index efdaf63..7cc194c 100644
--- a/android_webview/browser/aw_browser_process.cc
+++ b/android_webview/browser/aw_browser_process.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/aw_browser_process.h"
 
 #include "android_webview/browser/aw_browser_context.h"
+#include "android_webview/browser/aw_enterprise_authentication_app_link_manager.h"
 #include "android_webview/browser/component_updater/registration.h"
 #include "android_webview/browser/lifecycle/aw_contents_lifecycle_notifier.h"
 #include "android_webview/browser/metrics/visibility_metrics_logger.h"
@@ -58,6 +59,9 @@
   aw_contents_lifecycle_notifier_ =
       std::make_unique<AwContentsLifecycleNotifier>(base::BindRepeating(
           &AwBrowserProcess::OnLoseForeground, base::Unretained(this)));
+
+  app_link_manager_ =
+      absl::make_unique<EnterpriseAuthenticationAppLinkManager>(local_state());
 }
 
 AwBrowserProcess::~AwBrowserProcess() {
@@ -216,6 +220,11 @@
       CreateHttpAuthDynamicParams());
 }
 
+EnterpriseAuthenticationAppLinkManager*
+AwBrowserProcess::GetEnterpriseAuthenticationAppLinkManager() {
+  return app_link_manager_.get();
+}
+
 // static
 void AwBrowserProcess::TriggerMinidumpUploading() {
   Java_AwBrowserProcess_triggerMinidumpUploading(
diff --git a/android_webview/browser/aw_browser_process.h b/android_webview/browser/aw_browser_process.h
index c8e4109..8c5f0d6 100644
--- a/android_webview/browser/aw_browser_process.h
+++ b/android_webview/browser/aw_browser_process.h
@@ -9,6 +9,7 @@
 
 #include "android_webview/browser/aw_apk_type.h"
 #include "android_webview/browser/aw_browser_context.h"
+#include "android_webview/browser/aw_enterprise_authentication_app_link_manager.h"
 #include "android_webview/browser/aw_feature_list_creator.h"
 #include "android_webview/browser/lifecycle/aw_contents_lifecycle_notifier.h"
 #include "android_webview/browser/safe_browsing/aw_safe_browsing_allowlist_manager.h"
@@ -84,6 +85,9 @@
   static void TriggerMinidumpUploading();
   static ApkType GetApkType();
 
+  EnterpriseAuthenticationAppLinkManager*
+  GetEnterpriseAuthenticationAppLinkManager();
+
  private:
   void CreateSafeBrowsingUIManager();
   void CreateSafeBrowsingAllowlistManager();
@@ -122,6 +126,7 @@
 
   std::unique_ptr<VisibilityMetricsLogger> visibility_metrics_logger_;
   std::unique_ptr<AwContentsLifecycleNotifier> aw_contents_lifecycle_notifier_;
+  std::unique_ptr<EnterpriseAuthenticationAppLinkManager> app_link_manager_;
 };
 
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index d9bc031..ef7fcb0 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -64,6 +64,8 @@
 #include "components/safe_browsing/content/browser/browser_url_loader_throttle.h"
 #include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h"
 #include "components/safe_browsing/core/common/features.h"
+#include "components/url_matcher/url_matcher.h"
+#include "components/url_matcher/url_util.h"
 #include "content/public/browser/browser_associated_interface.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -632,30 +634,6 @@
   return safe_browsing_url_checker_delegate_;
 }
 
-static bool IsEnterpriseAuthAppLinkUrl(const GURL& url) {
-  PrefService* pref_service =
-      android_webview::AwBrowserProcess::GetInstance()->local_state();
-
-  const base::Value* authentication_url_list =
-      pref_service->GetList(prefs::kEnterpriseAuthAppLinkPolicy);
-
-  if (authentication_url_list == nullptr) {
-    return false;
-  }
-
-  for (const auto& el : authentication_url_list->GetList()) {
-    const std::string* policy_url = el.FindStringKey("url");
-    GURL authentication_url = GURL(*policy_url);
-
-    // TODO(ayushsha,b/201408457): Use UrlMatcher to match authentication urls.
-    if (authentication_url.EqualsIgnoringRef(url)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 bool AwContentBrowserClient::ShouldOverrideUrlLoading(
     int frame_tree_node_id,
     bool browser_initiated,
@@ -705,7 +683,9 @@
   AwSettings* aw_settings = AwSettings::FromWebContents(web_contents);
   if ((gurl.SchemeIs(url::kHttpScheme) || gurl.SchemeIs(url::kHttpsScheme)) &&
       aw_settings->enterprise_authentication_app_link_policy_enabled() &&
-      IsEnterpriseAuthAppLinkUrl(gurl)) {
+      android_webview::AwBrowserProcess::GetInstance()
+          ->GetEnterpriseAuthenticationAppLinkManager()
+          ->IsEnterpriseAuthenticationUrl(gurl)) {
     bool success = client_bridge->SendBrowseIntent(url);
     if (success) {
       return true;
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 0b9fc8e..72ed0e0e 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -915,36 +915,35 @@
 
 void AwContents::UpdateLastHitTestData(JNIEnv* env) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!render_view_host_ext_->HasNewHitTestData())
-    return;
 
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
   if (!obj)
     return;
 
-  const android_webview::mojom::HitTestData& data =
-      render_view_host_ext_->GetLastHitTestData();
-  render_view_host_ext_->MarkHitTestDataRead();
+  android_webview::mojom::HitTestDataPtr data =
+      render_view_host_ext_->TakeLastHitTestData();
+  if (!data)
+    return;
 
   // Make sure to null the Java object if data is empty/invalid.
   ScopedJavaLocalRef<jstring> extra_data_for_type;
-  if (data.extra_data_for_type.length())
+  if (data->extra_data_for_type.length())
     extra_data_for_type =
-        ConvertUTF8ToJavaString(env, data.extra_data_for_type);
+        ConvertUTF8ToJavaString(env, data->extra_data_for_type);
 
   ScopedJavaLocalRef<jstring> href;
-  if (data.href.length())
-    href = ConvertUTF16ToJavaString(env, data.href);
+  if (data->href.length())
+    href = ConvertUTF16ToJavaString(env, data->href);
 
   ScopedJavaLocalRef<jstring> anchor_text;
-  if (data.anchor_text.length())
-    anchor_text = ConvertUTF16ToJavaString(env, data.anchor_text);
+  if (data->anchor_text.length())
+    anchor_text = ConvertUTF16ToJavaString(env, data->anchor_text);
 
   ScopedJavaLocalRef<jstring> img_src;
-  if (data.img_src.is_valid())
-    img_src = ConvertUTF8ToJavaString(env, data.img_src.spec());
+  if (data->img_src.is_valid())
+    img_src = ConvertUTF8ToJavaString(env, data->img_src.spec());
 
-  Java_AwContents_updateHitTestData(env, obj, static_cast<jint>(data.type),
+  Java_AwContents_updateHitTestData(env, obj, static_cast<jint>(data->type),
                                     extra_data_for_type, href, anchor_text,
                                     img_src);
 }
diff --git a/android_webview/browser/aw_enterprise_authentication_app_link_manager.cc b/android_webview/browser/aw_enterprise_authentication_app_link_manager.cc
new file mode 100644
index 0000000..b8312292
--- /dev/null
+++ b/android_webview/browser/aw_enterprise_authentication_app_link_manager.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "android_webview/browser/aw_enterprise_authentication_app_link_manager.h"
+
+#include "components/url_matcher/url_util.h"
+
+namespace android_webview {
+
+namespace prefs {
+extern const char kEnterpriseAuthAppLinkPolicy[];
+}
+
+EnterpriseAuthenticationAppLinkManager::EnterpriseAuthenticationAppLinkManager(
+    PrefService* pref_service) {
+  pref_service_ = pref_service;
+  pref_observer_.Init(pref_service);
+  pref_observer_.Add(
+      prefs::kEnterpriseAuthAppLinkPolicy,
+      base::BindRepeating(
+          &EnterpriseAuthenticationAppLinkManager::OnPolicyUpdated,
+          base::Unretained(this)));
+
+  // Call once to initialize the watcher with the current pref's values.
+  OnPolicyUpdated();
+}
+
+EnterpriseAuthenticationAppLinkManager::
+    ~EnterpriseAuthenticationAppLinkManager() = default;
+
+void EnterpriseAuthenticationAppLinkManager::OnPolicyUpdated() {
+  const base::Value* authentication_urls_policy =
+      pref_service_->GetList(prefs::kEnterpriseAuthAppLinkPolicy);
+  if (authentication_urls_policy == nullptr) {
+    url_matcher_.reset(nullptr);
+    return;
+  }
+
+  url_matcher_ = std::make_unique<url_matcher::URLMatcher>();
+  url_matcher::util::AddAllowFilters(
+      url_matcher_.get(),
+      &base::Value::AsListValue(*authentication_urls_policy));
+}
+
+bool EnterpriseAuthenticationAppLinkManager::IsEnterpriseAuthenticationUrl(
+    const GURL& url) {
+  return url_matcher_ && !(url_matcher_->MatchURL(url).empty());
+}
+}  // namespace android_webview
\ No newline at end of file
diff --git a/android_webview/browser/aw_enterprise_authentication_app_link_manager.h b/android_webview/browser/aw_enterprise_authentication_app_link_manager.h
new file mode 100644
index 0000000..bc7c1c2
--- /dev/null
+++ b/android_webview/browser/aw_enterprise_authentication_app_link_manager.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_AW_ENTERPRISE_AUTHENTICATION_APP_LINK_MANAGER_H_
+#define ANDROID_WEBVIEW_BROWSER_AW_ENTERPRISE_AUTHENTICATION_APP_LINK_MANAGER_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "components/url_matcher/url_matcher.h"
+#include "url/gurl.h"
+
+namespace android_webview {
+
+class EnterpriseAuthenticationAppLinkManager {
+ public:
+  explicit EnterpriseAuthenticationAppLinkManager(PrefService* pref_service);
+
+  EnterpriseAuthenticationAppLinkManager(
+      const EnterpriseAuthenticationAppLinkManager&) = delete;
+  EnterpriseAuthenticationAppLinkManager& operator=(
+      const EnterpriseAuthenticationAppLinkManager&) = delete;
+  ~EnterpriseAuthenticationAppLinkManager();
+
+  bool IsEnterpriseAuthenticationUrl(const GURL& url);
+
+ private:
+  // Called when the policy value changes in Prefs.
+  void OnPolicyUpdated();
+
+  PrefChangeRegistrar pref_observer_;
+  std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
+  raw_ptr<PrefService> pref_service_;
+};
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_BROWSER_AW_ENTERPRISE_AUTHENTICATION_APP_LINK_MANAGER_H_
diff --git a/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc b/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc
new file mode 100644
index 0000000..1421c8102
--- /dev/null
+++ b/android_webview/browser/enterprise_authentication_app_link_policy_handler.cc
@@ -0,0 +1,89 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "android_webview/browser/enterprise_authentication_app_link_policy_handler.h"
+
+#include <memory>
+
+#include "base/strings/string_util.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "url/gurl.h"
+
+namespace policy {
+EnterpriseAuthenticationAppLinkPolicyHandler::
+    EnterpriseAuthenticationAppLinkPolicyHandler(const char* policy_name,
+                                                 const char* pref_path)
+    : TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
+      pref_path_(pref_path) {}
+
+EnterpriseAuthenticationAppLinkPolicyHandler::
+    ~EnterpriseAuthenticationAppLinkPolicyHandler() = default;
+
+bool EnterpriseAuthenticationAppLinkPolicyHandler::CheckPolicySettings(
+    const PolicyMap& policies,
+    PolicyErrorMap* errors) {
+  if (!TypeCheckingPolicyHandler::CheckPolicySettings(policies, errors))
+    return false;
+
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
+  if (!value || value->GetList().empty())
+    return true;
+
+  // Filters more than |url_util::kMaxFiltersPerPolicy| are ignored, add a
+  // warning message.
+  if (value->GetList().size() > policy::kMaxUrlFiltersPerPolicy) {
+    errors->AddError(policy_name(),
+                     IDS_POLICY_URL_ALLOW_BLOCK_LIST_MAX_FILTERS_LIMIT_WARNING,
+                     base::NumberToString(policy::kMaxUrlFiltersPerPolicy));
+  }
+
+  std::vector<std::string> invalid_policies;
+  for (const auto& entry : value->GetList()) {
+    if (!ValidatePolicyEntry(entry.FindStringKey("url")))
+      invalid_policies.push_back(entry.GetString());
+  }
+
+  if (!invalid_policies.empty()) {
+    errors->AddError(policy_name(), IDS_POLICY_PROTO_PARSING_ERROR,
+                     base::JoinString(invalid_policies, ","));
+  }
+
+  return invalid_policies.size() < value->GetList().size();
+}
+
+void EnterpriseAuthenticationAppLinkPolicyHandler::ApplyPolicySettings(
+    const PolicyMap& policies,
+    PrefValueMap* prefs) {
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
+  if (!value)
+    return;
+
+  std::vector<base::Value> filtered_values;
+  for (const auto& entry : value->GetList()) {
+    const std::string* url = entry.FindStringKey("url");
+    if (ValidatePolicyEntry(url))
+      filtered_values.emplace_back(*url);
+  }
+  if (filtered_values.size() > policy::kMaxUrlFiltersPerPolicy)
+    filtered_values.resize(policy::kMaxUrlFiltersPerPolicy);
+
+  prefs->SetValue(pref_path_, base::Value(std::move(filtered_values)));
+}
+
+// Validates that policy follows official pattern
+// https://www.chromium.org/administrators/url-blocklist-filter-format
+bool EnterpriseAuthenticationAppLinkPolicyHandler::ValidatePolicyEntry(
+    const std::string* policy) {
+  url_matcher::util::FilterComponents components;
+  return policy && url_matcher::util::FilterToComponents(
+                       *policy, &components.scheme, &components.host,
+                       &components.match_subdomains, &components.port,
+                       &components.path, &components.query);
+}
+
+}  // namespace policy
diff --git a/android_webview/browser/enterprise_authentication_app_link_policy_handler.h b/android_webview/browser/enterprise_authentication_app_link_policy_handler.h
new file mode 100644
index 0000000..6e60dae
--- /dev/null
+++ b/android_webview/browser/enterprise_authentication_app_link_policy_handler.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_HANDLER_H_
+#define ANDROID_WEBVIEW_BROWSER_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_HANDLER_H_
+
+#include "android_webview/browser/aw_browser_process.h"
+#include "components/policy/core/browser/configuration_policy_handler.h"
+#include "components/policy/core/browser/policy_error_map.h"
+#include "components/policy/core/browser/url_allowlist_policy_handler.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/url_matcher/url_util.h"
+
+namespace policy {
+
+// Policy handler for EnterpriseAuthenticationAppLink policy
+class EnterpriseAuthenticationAppLinkPolicyHandler
+    : public TypeCheckingPolicyHandler {
+ public:
+  EnterpriseAuthenticationAppLinkPolicyHandler(const char* policy_name,
+                                               const char* pref_path);
+
+  EnterpriseAuthenticationAppLinkPolicyHandler(
+      const EnterpriseAuthenticationAppLinkPolicyHandler&) = delete;
+  EnterpriseAuthenticationAppLinkPolicyHandler& operator=(
+      const EnterpriseAuthenticationAppLinkPolicyHandler&) = delete;
+  ~EnterpriseAuthenticationAppLinkPolicyHandler() override;
+
+  // ConfigurationPolicyHandler methods:
+  bool CheckPolicySettings(const PolicyMap& policies,
+                           PolicyErrorMap* errors) override;
+  void ApplyPolicySettings(const PolicyMap& policies,
+                           PrefValueMap* prefs) override;
+
+ private:
+  bool ValidatePolicyEntry(const std::string* policy);
+  const char* pref_path_;
+};
+
+}  // namespace policy
+
+#endif  // ANDROID_WEBVIEW_BROWSER_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_HANDLER_H_
diff --git a/android_webview/browser/enterprise_authentication_app_link_policy_handler_unittest.cc b/android_webview/browser/enterprise_authentication_app_link_policy_handler_unittest.cc
new file mode 100644
index 0000000..87b8d20
--- /dev/null
+++ b/android_webview/browser/enterprise_authentication_app_link_policy_handler_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "android_webview/browser/enterprise_authentication_app_link_policy_handler.h"
+
+#include "base/json/json_reader.h"
+#include "components/policy/core/browser/configuration_policy_pref_store.h"
+#include "components/policy/core/browser/configuration_policy_pref_store_test.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/policy_constants.h"
+
+namespace policy {
+
+class EnterpriseAuthenticationAppLinkPolicyHandlerTest
+    : public ConfigurationPolicyPrefStoreTest {
+  void SetUp() override {
+    Schema chrome_schema = Schema::Wrap(policy::GetChromeSchemaData());
+    handler_list_.AddHandler(base::WrapUnique<ConfigurationPolicyHandler>(
+        new EnterpriseAuthenticationAppLinkPolicyHandler(
+            policy::key::kEnterpriseAuthenticationAppLinkPolicy,
+            android_webview::prefs::kEnterpriseAuthAppLinkPolicy)));
+  }
+};
+
+TEST_F(EnterpriseAuthenticationAppLinkPolicyHandlerTest, ValidPolicy) {
+  PolicyMap policy;
+  policy.Set(policy::key::kEnterpriseAuthenticationAppLinkPolicy,
+             POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_PLATFORM,
+             base::JSONReader::Read(
+                 "["
+                 "  {"
+                 "    \"url\": \"https://www.testserver1.com/login\""
+                 "  },"
+                 "  {"
+                 "    \"url\": \"https://www.testserver2.com/login\""
+                 "  }"
+                 "]"),
+             nullptr);
+  this->UpdateProviderPolicy(policy);
+  const base::Value* pref_value = nullptr;
+  absl::optional<base::Value> expected = base::JSONReader::Read(R"(
+    [
+     "https://www.testserver1.com/login",
+     "https://www.testserver2.com/login"
+    ]
+  )");
+
+  EXPECT_TRUE(store_->GetValue(
+      android_webview::prefs::kEnterpriseAuthAppLinkPolicy, &pref_value));
+  ASSERT_TRUE(pref_value);
+  EXPECT_EQ(expected, *pref_value);
+}
+}  // namespace policy
\ No newline at end of file
diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
index 31c07ca..1d501843 100644
--- a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
+++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
@@ -7,6 +7,7 @@
 #include "android_webview/browser/aw_browser_context.h"
 #include "android_webview/browser/aw_contents.h"
 #include "android_webview/browser/aw_contents_client_bridge.h"
+#include "android_webview/common/aw_features.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -43,7 +44,6 @@
                                          content::WebContents* contents)
     : content::WebContentsObserver(contents),
       client_(client),
-      has_new_hit_test_data_(false),
       frame_host_receivers_(contents, this) {
   DCHECK(client_);
 }
@@ -68,18 +68,23 @@
   }
 }
 
-bool AwRenderViewHostExt::HasNewHitTestData() const {
-  return has_new_hit_test_data_;
-}
-
-void AwRenderViewHostExt::MarkHitTestDataRead() {
-  has_new_hit_test_data_ = false;
-}
-
 void AwRenderViewHostExt::RequestNewHitTestDataAt(
     const gfx::PointF& touch_center,
     const gfx::SizeF& touch_area) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // If the new hit testing approach for touch start is enabled we just early
+  // return.
+  if (base::FeatureList::IsEnabled(
+          features::kWebViewHitTestInBlinkOnTouchStart)) {
+    return;
+  }
+
+  // The following code is broken for OOPIF and fenced frames. The hit testing
+  // feature for touch start replaces this code and works correctly in those
+  // scenarios. For mitigating risk we've put the old code behind a feature
+  // flag.
+  //
   // We only need to get blink::WebView on the renderer side to invoke the
   // blink hit test Mojo method, so sending this message via LocalMainFrame
   // interface is enough.
@@ -87,9 +92,9 @@
     local_main_frame_remote->HitTest(touch_center, touch_area);
 }
 
-const mojom::HitTestData& AwRenderViewHostExt::GetLastHitTestData() const {
+mojom::HitTestDataPtr AwRenderViewHostExt::TakeLastHitTestData() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return *last_hit_test_data_;
+  return std::move(last_hit_test_data_);
 }
 
 void AwRenderViewHostExt::SetTextZoomFactor(float factor) {
@@ -145,19 +150,15 @@
 
 void AwRenderViewHostExt::UpdateHitTestData(
     mojom::HitTestDataPtr hit_test_data) {
-  content::RenderFrameHost* main_frame_host =
+  content::RenderFrameHost* render_frame_host =
       frame_host_receivers_.GetCurrentTargetFrame();
-  while (main_frame_host->GetParent())
-    main_frame_host = main_frame_host->GetParent();
-
-  // Make sense from any frame of the current frame tree, because a focused
+  // Make sense from any frame of the active frame tree, because a focused
   // node could be in either the mainframe or a subframe.
-  if (main_frame_host != web_contents()->GetPrimaryMainFrame())
+  if (!render_frame_host->IsActive())
     return;
 
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   last_hit_test_data_ = std::move(hit_test_data);
-  has_new_hit_test_data_ = true;
 }
 
 void AwRenderViewHostExt::ContentsSizeChanged(const gfx::Size& contents_size) {
diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.h b/android_webview/browser/renderer_host/aw_render_view_host_ext.h
index 0d0a881..41e903b 100644
--- a/android_webview/browser/renderer_host/aw_render_view_host_ext.h
+++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.h
@@ -62,14 +62,9 @@
   // independent pixels used by blink::WebView.
   void RequestNewHitTestDataAt(const gfx::PointF& touch_center,
                                const gfx::SizeF& touch_area);
-
-  // Optimization to avoid unnecessary Java object creation on hit test.
-  bool HasNewHitTestData() const;
-  void MarkHitTestDataRead();
-
   // Return |last_hit_test_data_|. Note that this is unavoidably racy;
   // the corresponding public WebView API is as well.
-  const mojom::HitTestData& GetLastHitTestData() const;
+  mojom::HitTestDataPtr TakeLastHitTestData();
 
   // Sets the zoom factor for text only. Used in layout modes other than
   // Text Autosizing.
@@ -112,8 +107,6 @@
   // is called in AwRenderViewExt.
   android_webview::mojom::HitTestDataPtr last_hit_test_data_;
 
-  bool has_new_hit_test_data_;
-
   // Some WebView users might want to show their own error pages / logic.
   bool will_suppress_error_page_ = false;
 
diff --git a/android_webview/browser/state_serializer.cc b/android_webview/browser/state_serializer.cc
index 1e2929c..9b3c088 100644
--- a/android_webview/browser/state_serializer.cc
+++ b/android_webview/browser/state_serializer.cc
@@ -318,7 +318,7 @@
 
   if (state_version >= internal::AW_STATE_VERSION_DATA_URL) {
     const char* data;
-    size_t size;
+    int size;
     if (!iterator->ReadData(&data, &size))
       return false;
     if (size > 0) {
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index 288d796..1c0d019 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -46,6 +46,9 @@
 const base::Feature kWebViewForceDarkModeMatchTheme{
     "WebViewForceDarkModeMatchTheme", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kWebViewHitTestInBlinkOnTouchStart{
+    "WebViewHitTestInBlinkOnTouchStart", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enable display cutout support for Android P and above.
 const base::Feature kWebViewDisplayCutout{"WebViewDisplayCutout",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index b703164..273acf9 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -27,6 +27,7 @@
 extern const base::Feature kWebViewEmptyComponentLoaderPolicy;
 extern const base::Feature kWebViewExtraHeadersSameOriginOnly;
 extern const base::Feature kWebViewForceDarkModeMatchTheme;
+extern const base::Feature kWebViewHitTestInBlinkOnTouchStart;
 extern const base::Feature kWebViewJavaJsBridgeMojo;
 extern const base::Feature kWebViewLegacyTlsSupport;
 extern const base::Feature kWebViewMeasureScreenCoverage;
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 29cb9717..80b9ba88 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -337,6 +337,8 @@
             Flag.baseFeature(BlinkFeatures.TIMED_HTML_PARSER_BUDGET,
                     "If enabled, the HTMLDocumentParser will use a budget based on elapsed time"
                             + " rather than token count."),
+            Flag.baseFeature(AwFeatures.WEBVIEW_HIT_TEST_IN_BLINK_ON_TOUCH_START,
+                    "Hit test on touch start in blink"),
             // Add new commandline switches and features above. The final entry should have a
             // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/renderer/aw_render_frame_ext.cc b/android_webview/renderer/aw_render_frame_ext.cc
index 8b3fd86..f0b366fe 100644
--- a/android_webview/renderer/aw_render_frame_ext.cc
+++ b/android_webview/renderer/aw_render_frame_ext.cc
@@ -7,6 +7,7 @@
 #include <map>
 #include <memory>
 
+#include "android_webview/common/aw_features.h"
 #include "android_webview/common/mojom/frame.mojom.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
@@ -216,6 +217,16 @@
   return registry_.TryBindInterface(interface_name, handle);
 }
 
+void AwRenderFrameExt::DidCreateDocumentElement() {
+  if (!base::FeatureList::IsEnabled(
+          features::kWebViewHitTestInBlinkOnTouchStart)) {
+    return;
+  }
+  render_frame()->GetWebFrame()->AddHitTestOnTouchStartCallback(
+      base::BindRepeating(&AwRenderFrameExt::HandleHitTestResult,
+                          base::Unretained(this)));
+}
+
 void AwRenderFrameExt::DidCommitProvisionalLoad(
     ui::PageTransition transition) {
   // Clear the cache when we cross site boundaries in the main frame.
@@ -268,6 +279,11 @@
   const blink::WebHitTestResult result = webview->HitTestResultForTap(
       gfx::Point(touch_center.x(), touch_center.y()),
       gfx::Size(touch_area.width(), touch_area.height()));
+  HandleHitTestResult(result);
+}
+
+void AwRenderFrameExt::HandleHitTestResult(
+    const blink::WebHitTestResult& result) {
   auto data = mojom::HitTestData::New();
 
   GURL absolute_image_url = result.AbsoluteImageURL();
diff --git a/android_webview/renderer/aw_render_frame_ext.h b/android_webview/renderer/aw_render_frame_ext.h
index e56de5cc..522c271b 100644
--- a/android_webview/renderer/aw_render_frame_ext.h
+++ b/android_webview/renderer/aw_render_frame_ext.h
@@ -19,6 +19,7 @@
 
 namespace blink {
 class WebFrameWidget;
+class WebHitTestResult;
 class WebView;
 }
 
@@ -47,6 +48,7 @@
   void DidCommitProvisionalLoad(ui::PageTransition transition) override;
 
   void FocusedElementChanged(const blink::WebElement& element) override;
+  void DidCreateDocumentElement() override;
   void OnDestruct() override;
 
   // mojom::LocalMainFrame overrides:
@@ -63,6 +65,8 @@
   void BindLocalMainFrame(
       mojo::PendingAssociatedReceiver<mojom::LocalMainFrame> pending_receiver);
 
+  void HandleHitTestResult(const blink::WebHitTestResult& result);
+
   const mojo::AssociatedRemote<mojom::FrameHost>& GetFrameHost();
 
   blink::WebView* GetWebView();
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index ebec051..86630bc3 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -569,6 +569,7 @@
     "//components/metrics",
     "//components/metrics:component_metrics",
     "//components/optimization_guide/core:bloomfilter",
+    "//components/policy/core/browser:test_support",
     "//components/prefs:prefs",
     "//components/prefs:test_support",
     "//components/safe_browsing/content/browser/web_ui",
@@ -601,6 +602,7 @@
     "../browser/aw_pac_processor_unittest.cc",
     "../browser/aw_permission_manager_unittest.cc",
     "../browser/component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy_unittest.cc",
+    "../browser/enterprise_authentication_app_link_policy_handler_unittest.cc",
     "../browser/gfx/begin_frame_source_webview_unittest.cc",
     "../browser/gfx/browser_view_renderer_unittest.cc",
     "../browser/gfx/test/fake_window.cc",
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 7d28210..f862b96 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -429,6 +429,11 @@
 const base::Feature kCryptohomeRecoveryFlow{"CryptohomeRecoveryFlow",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the UI for the cryptohome recovery feature:
+// - New UI for Gaia password changed screen.
+const base::Feature kCryptohomeRecoveryFlowUI{
+    "CryptohomeRecoveryFlowUI", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables the UI to enable or disable cryptohome recovery in the settings
 // page. Also guards the wiring of cryptohome recovery settings to the
 // cryptohome backend.
@@ -1763,6 +1768,10 @@
   return base::FeatureList::IsEnabled(kCryptohomeRecoveryFlow);
 }
 
+bool IsCryptohomeRecoveryFlowUIEnabled() {
+  return base::FeatureList::IsEnabled(kCryptohomeRecoveryFlowUI);
+}
+
 bool IsCryptohomeRecoverySetupEnabled() {
   return base::FeatureList::IsEnabled(kCryptohomeRecoverySetup);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 8d98fd4..f10ed12 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -175,6 +175,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptohomeRecoveryFlow;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kCryptohomeRecoveryFlowUI;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptohomeRecoverySetup;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDemoModeSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -670,6 +672,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsConsumerAutoUpdateToggleAllowed();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDesksCloseAllEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCryptohomeRecoveryFlowEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCryptohomeRecoveryFlowUIEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCryptohomeRecoverySetupEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDarkLightModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeepLinkingEnabled();
diff --git a/ash/strings/ash_strings_da.xtb b/ash/strings/ash_strings_da.xtb
index fdefa9d..17979a23 100644
--- a/ash/strings/ash_strings_da.xtb
+++ b/ash/strings/ash_strings_da.xtb
@@ -536,6 +536,7 @@
 <translation id="4115378294792113321">Magenta</translation>
 <translation id="412298498316631026">vindue</translation>
 <translation id="4123259114412175274">Bluetooth skal aktiveres, før du kan låse din Chromebook op</translation>
+<translation id="4125970834901680537">Apps kan ikke streames i tablettilstand. Prøv igen i computertilstand.</translation>
 <translation id="4129129681837227511">Du kan se notifikationer på din låseskærm ved at låse skærmen op og ændre indstillingen</translation>
 <translation id="4136724716305260864">Aktiveret indtil solopgang</translation>
 <translation id="4146833061457621061">Spil musik</translation>
@@ -553,6 +554,7 @@
 <translation id="4215497585250573029">VPN-indstillinger</translation>
 <translation id="4217571870635786043">Diktering</translation>
 <translation id="4221957499226645091"><ph name="APP_NAME" />, installeret app, sat på pause</translation>
+<translation id="423685346499232137">Det er ikke muligt at kopiere eller indsætte indhold på nuværende tidspunkt</translation>
 <translation id="4239069858505860023">GPRS</translation>
 <translation id="4242533952199664413">Åbn Indstillinger</translation>
 <translation id="4250229828105606438">Screenshot</translation>
diff --git a/ash/strings/ash_strings_mn.xtb b/ash/strings/ash_strings_mn.xtb
index 20560a3..e2fc58d 100644
--- a/ash/strings/ash_strings_mn.xtb
+++ b/ash/strings/ash_strings_mn.xtb
@@ -537,6 +537,7 @@
 <translation id="4115378294792113321">Гүн ягаавтар улаан өнгө</translation>
 <translation id="412298498316631026">цонх</translation>
 <translation id="4123259114412175274">Өөрийн Chromebook-н түгжээг тайлахын тулд утасныхаа Bluetooth-г асаалттай эсэхийг шалгана уу</translation>
+<translation id="4125970834901680537">Таблет горимд апп дамжуулах боломжгүй. Зөөврийн компьютерын горимд дахин оролдоно уу.</translation>
 <translation id="4129129681837227511">Мэдэгдлийг түгжигдсэн дэлгэц дээрээ харахын тулд төхөөрөмжийн түгжээг тайлж, тохиргоог өөрчилнө үү</translation>
 <translation id="4136724716305260864">Нар мандах хүртэл асаалттай</translation>
 <translation id="4146833061457621061">Хөгжим тоглуулах</translation>
@@ -554,6 +555,7 @@
 <translation id="4215497585250573029">VPN тохиргоо</translation>
 <translation id="4217571870635786043">Заавар</translation>
 <translation id="4221957499226645091"><ph name="APP_NAME" />, Суулгасан апп, Түр зогсоосон</translation>
+<translation id="423685346499232137">Одоогоор контентыг хуулах эсвэл буулгах боломжгүй</translation>
 <translation id="4239069858505860023">GPRS</translation>
 <translation id="4242533952199664413">Нээлттэй тохиргоо</translation>
 <translation id="4250229828105606438">Дэлгэцийн агшин</translation>
diff --git a/ash/strings/ash_strings_uz.xtb b/ash/strings/ash_strings_uz.xtb
index 1ec37ae..f5bbdb443 100644
--- a/ash/strings/ash_strings_uz.xtb
+++ b/ash/strings/ash_strings_uz.xtb
@@ -536,6 +536,7 @@
 <translation id="4115378294792113321">Siyohrang</translation>
 <translation id="412298498316631026">oyna</translation>
 <translation id="4123259114412175274">Chromebook qurilmangiz qulfini ochish uchun telefoningizda Bluetooth yoniqligini. tekshiring</translation>
+<translation id="4125970834901680537">Planshet rejimida ilovalar translatsiyasi ishlamaydi. Laptop rejimida qaytadan urining.</translation>
 <translation id="4129129681837227511">Bildirishnomalar qulflangan ekranda chiqishi uchun avval qulfdan chiqaring va sozlamani yangilang</translation>
 <translation id="4136724716305260864">Quyosh botishigacha yoniq</translation>
 <translation id="4146833061457621061">Musiqani tinglash</translation>
@@ -553,6 +554,7 @@
 <translation id="4215497585250573029">VPN sozlamalari</translation>
 <translation id="4217571870635786043">Ovoz bilan yozish</translation>
 <translation id="4221957499226645091"><ph name="APP_NAME" />, Oʻrnatilgan ilova, Pauzada</translation>
+<translation id="423685346499232137">Ayni vaqtda kontentni nusxalash yoki joylash imkonsiz</translation>
 <translation id="4239069858505860023">GPRS</translation>
 <translation id="4242533952199664413">Sozlamalarni ochish</translation>
 <translation id="4250229828105606438">Skrinshot</translation>
diff --git a/ash/webui/demo_mode_app_ui/BUILD.gn b/ash/webui/demo_mode_app_ui/BUILD.gn
index efc16929..8bf36369 100644
--- a/ash/webui/demo_mode_app_ui/BUILD.gn
+++ b/ash/webui/demo_mode_app_ui/BUILD.gn
@@ -12,10 +12,10 @@
 
 static_library("demo_mode_app_ui") {
   sources = [
-    "demo_mode_app_ui.cc",
-    "demo_mode_app_ui.h",
-    "demo_mode_page_handler.cc",
-    "demo_mode_page_handler.h",
+    "demo_mode_app_untrusted_ui.cc",
+    "demo_mode_app_untrusted_ui.h",
+    "demo_mode_untrusted_page_handler.cc",
+    "demo_mode_untrusted_page_handler.h",
     "url_constants.cc",
     "url_constants.h",
   ]
@@ -32,7 +32,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "demo_mode_app_ui_unittests.cc" ]
+  sources = [ "demo_mode_app_untrusted_ui_unittests.cc" ]
   deps = [
     ":demo_mode_app_ui",
     "//base",
@@ -59,8 +59,7 @@
   out_grd = mojo_grdp_file
   grd_prefix = grd_prefix
   deps = [ "//ash/webui/demo_mode_app_ui/mojom:mojom_webui_js" ]
-  input_files =
-      [ "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom-webui.js" ]
+  input_files = [ "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom-webui.js" ]
   input_files_base_dir =
       rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir")
 }
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_app_ui.h b/ash/webui/demo_mode_app_ui/demo_mode_app_ui.h
deleted file mode 100644
index f69ce29..0000000
--- a/ash/webui/demo_mode_app_ui/demo_mode_app_ui.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_APP_UI_H_
-#define ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_APP_UI_H_
-
-#include "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom.h"
-#include "base/files/file_path.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/webui_config.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "ui/webui/mojo_web_ui_controller.h"
-
-namespace ash {
-
-class DemoModeAppUIConfig : public content::WebUIConfig {
- public:
-  explicit DemoModeAppUIConfig(
-      base::RepeatingCallback<base::FilePath()> component_path_producer);
-  ~DemoModeAppUIConfig() override;
-
-  std::unique_ptr<content::WebUIController> CreateWebUIController(
-      content::WebUI* web_ui) override;
-
-  bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
-
- private:
-  // Callback that provides the demo app component path to the WebUI controller.
-  // The path can't be passed directly into the DemoModeAppUIConfig constructor
-  // because the config is created during startup, whereas the component isn't
-  // loaded until the active demo session has started
-  //
-  // TODO(b/234174220): Consider creating a Delegate class that provides the
-  // component path instead
-  base::RepeatingCallback<base::FilePath()> component_path_producer_;
-};
-
-// The WebUI for chrome://demo-mode-app
-class DemoModeAppUI : public ui::MojoWebUIController,
-                      public mojom::demo_mode::PageHandlerFactory {
- public:
-  explicit DemoModeAppUI(content::WebUI* web_ui,
-                         base::FilePath component_base_path);
-  ~DemoModeAppUI() override;
-
-  DemoModeAppUI(const DemoModeAppUI&) = delete;
-  DemoModeAppUI& operator=(const DemoModeAppUI&) = delete;
-
-  void BindInterface(
-      mojo::PendingReceiver<mojom::demo_mode::PageHandlerFactory> factory);
-
-  // Visible for testing
-  static void SourceDataFromComponent(
-      const base::FilePath& component_path,
-      const std::string& resource_path,
-      content::WebUIDataSource::GotDataCallback callback);
-
- private:
-  // mojom::DemoModePageHandlerFactory
-  void CreatePageHandler(
-      mojo::PendingReceiver<mojom::demo_mode::PageHandler> handler) override;
-
-  mojo::Receiver<mojom::demo_mode::PageHandlerFactory> demo_mode_page_factory_{
-      this};
-
-  std::unique_ptr<mojom::demo_mode::PageHandler> demo_mode_page_handler_;
-
-  WEB_UI_CONTROLLER_TYPE_DECL();
-};
-
-}  // namespace ash
-
-#endif  // ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_APP_UI_H_
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc b/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.cc
similarity index 70%
rename from ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc
rename to ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.cc
index c5534d8c..ce5a26c9 100644
--- a/ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc
+++ b/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/webui/demo_mode_app_ui/demo_mode_app_ui.h"
+#include "ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h"
 
 #include <memory>
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/demo_mode_app_ui/demo_mode_page_handler.h"
+#include "ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.h"
 #include "ash/webui/demo_mode_app_ui/url_constants.h"
 #include "ash/webui/grit/ash_demo_mode_app_resources.h"
 #include "ash/webui/grit/ash_demo_mode_app_resources_map.h"
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/task/thread_pool.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/url_constants.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
@@ -23,20 +24,21 @@
 
 namespace ash {
 
-DemoModeAppUIConfig::DemoModeAppUIConfig(
+DemoModeAppUntrustedUIConfig::DemoModeAppUntrustedUIConfig(
     base::RepeatingCallback<base::FilePath()> component_path_producer)
-    : content::WebUIConfig(content::kChromeUIScheme, kChromeUIDemoModeAppHost),
+    : content::WebUIConfig(content::kChromeUIUntrustedScheme,
+                           kChromeUntrustedUIDemoModeAppHost),
       component_path_producer_(std::move(component_path_producer)) {}
 
-DemoModeAppUIConfig::~DemoModeAppUIConfig() = default;
+DemoModeAppUntrustedUIConfig::~DemoModeAppUntrustedUIConfig() = default;
 
 std::unique_ptr<content::WebUIController>
-DemoModeAppUIConfig::CreateWebUIController(content::WebUI* web_ui) {
-  return std::make_unique<DemoModeAppUI>(web_ui,
-                                         component_path_producer_.Run());
+DemoModeAppUntrustedUIConfig::CreateWebUIController(content::WebUI* web_ui) {
+  return std::make_unique<DemoModeAppUntrustedUI>(
+      web_ui, component_path_producer_.Run());
 }
 
-bool DemoModeAppUIConfig::IsWebUIEnabled(
+bool DemoModeAppUntrustedUIConfig::IsWebUIEnabled(
     content::BrowserContext* browser_context) {
   return ash::features::IsDemoModeSWAEnabled();
 }
@@ -57,7 +59,7 @@
   return !webui_resource_paths.contains(path);
 }
 
-void DemoModeAppUI::SourceDataFromComponent(
+void DemoModeAppUntrustedUI::SourceDataFromComponent(
     const base::FilePath& component_path,
     const std::string& resource_path,
     content::WebUIDataSource::GotDataCallback callback) {
@@ -65,7 +67,7 @@
   //
   // TODO (b/234170189): Verify that query params won't be used in the prod Demo
   // App, or add support for them here instead of ignoring them.
-  GURL full_url = GURL(kChromeUIDemoModeAppURL + resource_path);
+  GURL full_url = GURL(kChromeUntrustedUIDemoModeAppURL + resource_path);
   // Trim leading slash from path
   std::string path = full_url.path().substr(1);
 
@@ -76,16 +78,16 @@
       base::BindOnce(&ReadFile, absolute_resource_path), std::move(callback));
 }
 
-DemoModeAppUI::DemoModeAppUI(content::WebUI* web_ui,
-                             base::FilePath component_path)
-    : ui::MojoWebUIController(web_ui) {
+DemoModeAppUntrustedUI::DemoModeAppUntrustedUI(content::WebUI* web_ui,
+                                               base::FilePath component_path)
+    : ui::UntrustedWebUIController(web_ui) {
   // We tack the resource path onto this component path, so CHECK that it's
   // absolute so ".." parent references can't be used as an exploit
   DCHECK(component_path.IsAbsolute());
   content::WebUIDataSource* data_source =
       content::WebUIDataSource::CreateAndAdd(
           web_ui->GetWebContents()->GetBrowserContext(),
-          kChromeUIDemoModeAppHost);
+          kChromeUntrustedUIDemoModeAppURL);
 
   base::flat_set<std::string> webui_resource_paths;
   // Add required resources.
@@ -97,7 +99,7 @@
 
   data_source->SetDefaultResource(IDR_ASH_DEMO_MODE_APP_DEMO_MODE_APP_HTML);
   // Add empty string so default resource is still shown for
-  // chrome://demo-mode-app
+  // chrome-untrusted://demo-mode-app
   webui_resource_paths.insert("");
 
   data_source->SetRequestFilter(
@@ -105,24 +107,25 @@
       base::BindRepeating(&SourceDataFromComponent, component_path));
 }
 
-DemoModeAppUI::~DemoModeAppUI() = default;
+DemoModeAppUntrustedUI::~DemoModeAppUntrustedUI() = default;
 
-void DemoModeAppUI::BindInterface(
-    mojo::PendingReceiver<mojom::demo_mode::PageHandlerFactory> factory) {
+void DemoModeAppUntrustedUI::BindInterface(
+    mojo::PendingReceiver<mojom::demo_mode::UntrustedPageHandlerFactory>
+        factory) {
   if (demo_mode_page_factory_.is_bound()) {
     demo_mode_page_factory_.reset();
   }
   demo_mode_page_factory_.Bind(std::move(factory));
 }
 
-void DemoModeAppUI::CreatePageHandler(
-    mojo::PendingReceiver<mojom::demo_mode::PageHandler> handler) {
+void DemoModeAppUntrustedUI::CreatePageHandler(
+    mojo::PendingReceiver<mojom::demo_mode::UntrustedPageHandler> handler) {
   views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
       web_ui()->GetWebContents()->GetTopLevelNativeWindow());
-  demo_mode_page_handler_ =
-      std::make_unique<DemoModePageHandler>(std::move(handler), widget);
+  demo_mode_page_handler_ = std::make_unique<DemoModeUntrustedPageHandler>(
+      std::move(handler), widget);
 }
 
-WEB_UI_CONTROLLER_TYPE_IMPL(DemoModeAppUI)
+WEB_UI_CONTROLLER_TYPE_IMPL(DemoModeAppUntrustedUI)
 
 }  // namespace ash
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h b/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h
new file mode 100644
index 0000000..1cacd63
--- /dev/null
+++ b/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h
@@ -0,0 +1,80 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_APP_UNTRUSTED_UI_H_
+#define ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_APP_UNTRUSTED_UI_H_
+
+#include "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom.h"
+#include "base/files/file_path.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/webui_config.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "ui/webui/untrusted_web_ui_controller.h"
+
+namespace ash {
+
+class DemoModeAppUntrustedUIConfig : public content::WebUIConfig {
+ public:
+  explicit DemoModeAppUntrustedUIConfig(
+      base::RepeatingCallback<base::FilePath()> component_path_producer);
+  ~DemoModeAppUntrustedUIConfig() override;
+
+  std::unique_ptr<content::WebUIController> CreateWebUIController(
+      content::WebUI* web_ui) override;
+
+  bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
+
+ private:
+  // Callback that provides the demo app component path to the WebUI controller.
+  // The path can't be passed directly into the DemoModeAppUntrustedUIConfig
+  // constructor because the config is created during startup, whereas the
+  // component isn't loaded until the active demo session has started
+  //
+  // TODO(b/234174220): Consider creating a Delegate class that provides the
+  // component path instead
+  base::RepeatingCallback<base::FilePath()> component_path_producer_;
+};
+
+// The WebUI for chrome-untrusted://demo-mode-app
+class DemoModeAppUntrustedUI
+    : public ui::UntrustedWebUIController,
+      public mojom::demo_mode::UntrustedPageHandlerFactory {
+ public:
+  explicit DemoModeAppUntrustedUI(content::WebUI* web_ui,
+                                  base::FilePath component_base_path);
+  ~DemoModeAppUntrustedUI() override;
+
+  DemoModeAppUntrustedUI(const DemoModeAppUntrustedUI&) = delete;
+  DemoModeAppUntrustedUI& operator=(const DemoModeAppUntrustedUI&) = delete;
+
+  void BindInterface(
+      mojo::PendingReceiver<mojom::demo_mode::UntrustedPageHandlerFactory>
+          factory);
+
+  // Visible for testing
+  static void SourceDataFromComponent(
+      const base::FilePath& component_path,
+      const std::string& resource_path,
+      content::WebUIDataSource::GotDataCallback callback);
+
+ private:
+  // mojom::DemoModePageHandlerFactory
+  void CreatePageHandler(
+      mojo::PendingReceiver<mojom::demo_mode::UntrustedPageHandler> handler)
+      override;
+
+  mojo::Receiver<mojom::demo_mode::UntrustedPageHandlerFactory>
+      demo_mode_page_factory_{this};
+
+  std::unique_ptr<mojom::demo_mode::UntrustedPageHandler>
+      demo_mode_page_handler_;
+
+  WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_APP_UNTRUSTED_UI_H_
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_app_ui_unittests.cc b/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui_unittests.cc
similarity index 77%
rename from ash/webui/demo_mode_app_ui/demo_mode_app_ui_unittests.cc
rename to ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui_unittests.cc
index 992fcf6..535ebf1 100644
--- a/ash/webui/demo_mode_app_ui/demo_mode_app_ui_unittests.cc
+++ b/ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui_unittests.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 #include <string>
 
-#include "ash/webui/demo_mode_app_ui/demo_mode_app_ui.h"
+#include "ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h"
 #include "base/callback.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -18,10 +18,10 @@
 
 const std::string kFileContents = "Test File Contents";
 
-class DemoModeAppUITest : public testing::Test {
+class DemoModeAppUntrustedUITest : public testing::Test {
  protected:
-  DemoModeAppUITest() = default;
-  ~DemoModeAppUITest() override = default;
+  DemoModeAppUntrustedUITest() = default;
+  ~DemoModeAppUntrustedUITest() override = default;
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -30,7 +30,7 @@
     base::WriteFile(content_file_path_, kFileContents);
 
     scheme_registry_ = std::make_unique<url::ScopedSchemeRegistryForTests>();
-    url::AddStandardScheme("chrome", url::SCHEME_WITH_HOST);
+    url::AddStandardScheme("chrome-untrusted", url::SCHEME_WITH_HOST);
   }
 
   base::FilePath content_file_path_;
@@ -47,53 +47,54 @@
   std::move(quit_closure).Run();
 }
 
-TEST_F(DemoModeAppUITest, SourceDataFromComponent) {
+TEST_F(DemoModeAppUntrustedUITest, SourceDataFromComponent) {
   base::RunLoop run_loop;
-  DemoModeAppUI::SourceDataFromComponent(
+  DemoModeAppUntrustedUI::SourceDataFromComponent(
       temp_dir_.GetPath(), content_file_path_.BaseName().MaybeAsASCII(),
       base::BindOnce(&VerifyDataResponse, kFileContents,
                      run_loop.QuitClosure()));
   run_loop.Run();
 }
 
-TEST_F(DemoModeAppUITest, SourceDataFromComponentQueryParam) {
+TEST_F(DemoModeAppUntrustedUITest, SourceDataFromComponentQueryParam) {
   base::RunLoop run_loop;
   std::string resource_path_with_query_param =
       content_file_path_.BaseName().MaybeAsASCII() + "?testparam=testvalue";
 
-  DemoModeAppUI::SourceDataFromComponent(
+  DemoModeAppUntrustedUI::SourceDataFromComponent(
       temp_dir_.GetPath(), resource_path_with_query_param,
       base::BindOnce(&VerifyDataResponse, kFileContents,
                      run_loop.QuitClosure()));
   run_loop.Run();
 }
 
-TEST_F(DemoModeAppUITest, SourceDataFromComponentURLFragment) {
+TEST_F(DemoModeAppUntrustedUITest, SourceDataFromComponentURLFragment) {
   base::RunLoop run_loop;
   std::string resource_path_with_url_fragment =
       content_file_path_.BaseName().MaybeAsASCII() + "#frag";
 
-  DemoModeAppUI::SourceDataFromComponent(
+  DemoModeAppUntrustedUI::SourceDataFromComponent(
       temp_dir_.GetPath(), resource_path_with_url_fragment,
       base::BindOnce(&VerifyDataResponse, kFileContents,
                      run_loop.QuitClosure()));
   run_loop.Run();
 }
 
-TEST_F(DemoModeAppUITest, SourceDataFromComponentQueryParamAndURLFragment) {
+TEST_F(DemoModeAppUntrustedUITest,
+       SourceDataFromComponentQueryParamAndURLFragment) {
   base::RunLoop run_loop;
   std::string resource_path_with_url_fragment =
       content_file_path_.BaseName().MaybeAsASCII() +
       "?testparam=testvalue#frag";
 
-  DemoModeAppUI::SourceDataFromComponent(
+  DemoModeAppUntrustedUI::SourceDataFromComponent(
       temp_dir_.GetPath(), resource_path_with_url_fragment,
       base::BindOnce(&VerifyDataResponse, kFileContents,
                      run_loop.QuitClosure()));
   run_loop.Run();
 }
 
-TEST_F(DemoModeAppUITest, SourceDataFromComponentParentDirReference) {
+TEST_F(DemoModeAppUntrustedUITest, SourceDataFromComponentParentDirReference) {
   base::RunLoop run_loop;
   // Treat temp_dir_ as the parent of the component directory here, that
   // a malicious ".."-containing path may be trying to access
@@ -102,7 +103,7 @@
   std::string resource_path_with_parent_ref =
       "../" + content_file_path_.BaseName().MaybeAsASCII();
 
-  DemoModeAppUI::SourceDataFromComponent(
+  DemoModeAppUntrustedUI::SourceDataFromComponent(
       component_dir.GetPath(), resource_path_with_parent_ref,
       base::BindOnce(&VerifyDataResponse, "", run_loop.QuitClosure()));
   run_loop.Run();
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_page_handler.cc b/ash/webui/demo_mode_app_ui/demo_mode_page_handler.cc
deleted file mode 100644
index c365ba1..0000000
--- a/ash/webui/demo_mode_app_ui/demo_mode_page_handler.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/webui/demo_mode_app_ui/demo_mode_page_handler.h"
-
-namespace ash {
-
-DemoModePageHandler::DemoModePageHandler(
-    mojo::PendingReceiver<mojom::demo_mode::PageHandler> pending_receiver,
-    views::Widget* widget)
-    : receiver_(this, std::move(pending_receiver)), widget_(widget) {}
-
-DemoModePageHandler::~DemoModePageHandler() = default;
-
-void DemoModePageHandler::ToggleFullscreen() {
-  widget_->SetFullscreen(!widget_->IsFullscreen());
-}
-
-}  // namespace ash
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_page_handler.h b/ash/webui/demo_mode_app_ui/demo_mode_page_handler.h
deleted file mode 100644
index 0334f43..0000000
--- a/ash/webui/demo_mode_app_ui/demo_mode_page_handler.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_PAGE_HANDLER_H_
-#define ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_PAGE_HANDLER_H_
-
-#include "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom.h"
-
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-
-class DemoModePageHandler : public mojom::demo_mode::PageHandler {
- public:
-  DemoModePageHandler(
-      mojo::PendingReceiver<mojom::demo_mode::PageHandler> pending_receiver,
-      views::Widget* widget);
-  ~DemoModePageHandler() override;
-
-  explicit DemoModePageHandler(const PageHandler&) = delete;
-  DemoModePageHandler& operator=(const PageHandler&) = delete;
-
- private:
-  // Switch between fullscreen and not-fullscreen
-  void ToggleFullscreen() override;
-
-  mojo::Receiver<mojom::demo_mode::PageHandler> receiver_;
-
-  views::Widget* widget_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_PAGE_HANDLER_H_
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.cc b/ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.cc
new file mode 100644
index 0000000..c9f4435
--- /dev/null
+++ b/ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.cc
@@ -0,0 +1,21 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.h"
+
+namespace ash {
+
+DemoModeUntrustedPageHandler::DemoModeUntrustedPageHandler(
+    mojo::PendingReceiver<mojom::demo_mode::UntrustedPageHandler>
+        pending_receiver,
+    views::Widget* widget)
+    : receiver_(this, std::move(pending_receiver)), widget_(widget) {}
+
+DemoModeUntrustedPageHandler::~DemoModeUntrustedPageHandler() = default;
+
+void DemoModeUntrustedPageHandler::ToggleFullscreen() {
+  widget_->SetFullscreen(!widget_->IsFullscreen());
+}
+
+}  // namespace ash
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.h b/ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.h
new file mode 100644
index 0000000..cf88f7a
--- /dev/null
+++ b/ash/webui/demo_mode_app_ui/demo_mode_untrusted_page_handler.h
@@ -0,0 +1,39 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_UNTRUSTED_PAGE_HANDLER_H_
+#define ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_UNTRUSTED_PAGE_HANDLER_H_
+
+#include "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom.h"
+
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+class DemoModeUntrustedPageHandler
+    : public mojom::demo_mode::UntrustedPageHandler {
+ public:
+  DemoModeUntrustedPageHandler(
+      mojo::PendingReceiver<mojom::demo_mode::UntrustedPageHandler>
+          pending_receiver,
+      views::Widget* widget);
+  ~DemoModeUntrustedPageHandler() override;
+
+  explicit DemoModeUntrustedPageHandler(const UntrustedPageHandler&) = delete;
+  DemoModeUntrustedPageHandler& operator=(const UntrustedPageHandler&) = delete;
+
+ private:
+  // Switch between fullscreen and not-fullscreen
+  void ToggleFullscreen() override;
+
+  mojo::Receiver<mojom::demo_mode::UntrustedPageHandler> receiver_;
+
+  views::Widget* widget_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_DEMO_MODE_APP_UI_DEMO_MODE_UNTRUSTED_PAGE_HANDLER_H_
diff --git a/ash/webui/demo_mode_app_ui/mojom/BUILD.gn b/ash/webui/demo_mode_app_ui/mojom/BUILD.gn
index fdffced7..63b69f1 100644
--- a/ash/webui/demo_mode_app_ui/mojom/BUILD.gn
+++ b/ash/webui/demo_mode_app_ui/mojom/BUILD.gn
@@ -9,7 +9,7 @@
 assert(!is_official_build, "Demo Mode App is only built for unofficial builds")
 
 mojom("mojom") {
-  sources = [ "demo_mode_app_ui.mojom" ]
+  sources = [ "demo_mode_app_untrusted_ui.mojom" ]
   public_deps = [ "//mojo/public/mojom/base" ]
   webui_module_path = "/ash/webui/demo_mode_app_ui/mojom/"
 }
diff --git a/ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom b/ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom
similarity index 78%
rename from ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom
rename to ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom
index 0b86fd3e..d2d7d6d 100644
--- a/ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom
+++ b/ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom
@@ -7,15 +7,15 @@
 // Implemented in the browser process. Interface for the Demo Mode WebUI to
 // retrieve an endpoint to the PageHandler interface, and to provide its own
 // Page endpoint to receive notifications.
-interface PageHandlerFactory {
+interface UntrustedPageHandlerFactory {
   // Create a page handler to provide one-way message passing between the WebUI
   // and browser process
-  CreatePageHandler(pending_receiver<PageHandler> handler);
+  CreatePageHandler(pending_receiver<UntrustedPageHandler> handler);
 };
 
-// Implemented in the Browser process. Interface for sending ToggleFullscreen()
+// Implemented in the browser process. Interface for sending ToggleFullscreen()
 // commands from the Web UI to the browser process.
-interface PageHandler {
+interface UntrustedPageHandler {
   // Fire-and-forget command to trigger enter or exit fullscreen on a native
   // window. Used for when we need to enter fullscreen without user interaction.
   ToggleFullscreen();
diff --git a/ash/webui/demo_mode_app_ui/resources/page_handler.js b/ash/webui/demo_mode_app_ui/resources/page_handler.js
index ff6985de..d5f9167 100644
--- a/ash/webui/demo_mode_app_ui/resources/page_handler.js
+++ b/ash/webui/demo_mode_app_ui/resources/page_handler.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageHandlerFactory, PageHandlerRemote} from '/ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom-webui.js';
+import {UntrustedPageHandlerFactory, UntrustedPageHandlerRemote} from '/ash/webui/demo_mode_app_ui/mojom/demo_mode_app_untrusted_ui.mojom-webui.js';
 
 /**
  * Provides interfaces for sending and receiving messages to/from the browser
@@ -10,9 +10,9 @@
  */
 class PageHandler {
   constructor() {
-    this.handler = new PageHandlerRemote();
+    this.handler = new UntrustedPageHandlerRemote();
 
-    const factoryRemote = PageHandlerFactory.getRemote();
+    const factoryRemote = UntrustedPageHandlerFactory.getRemote();
     factoryRemote.createPageHandler(
         this.handler.$.bindNewPipeAndPassReceiver());
   }
diff --git a/ash/webui/demo_mode_app_ui/url_constants.cc b/ash/webui/demo_mode_app_ui/url_constants.cc
index 66da17e..6d004bf 100644
--- a/ash/webui/demo_mode_app_ui/url_constants.cc
+++ b/ash/webui/demo_mode_app_ui/url_constants.cc
@@ -6,8 +6,8 @@
 
 namespace ash {
 
-const char kChromeUIDemoModeAppHost[] = "demo-mode-app";
-const char kChromeUIDemoModeAppURL[] = "chrome://demo-mode-app/";
-// TODO(b/232019361): Add untrusted demo mode app url to constants here
+const char kChromeUntrustedUIDemoModeAppHost[] = "demo-mode-app";
+const char kChromeUntrustedUIDemoModeAppURL[] =
+    "chrome-untrusted://demo-mode-app/";
 
 }  // namespace ash
diff --git a/ash/webui/demo_mode_app_ui/url_constants.h b/ash/webui/demo_mode_app_ui/url_constants.h
index 3077d4e..6ec21e9 100644
--- a/ash/webui/demo_mode_app_ui/url_constants.h
+++ b/ash/webui/demo_mode_app_ui/url_constants.h
@@ -7,8 +7,8 @@
 
 namespace ash {
 
-extern const char kChromeUIDemoModeAppHost[];
-extern const char kChromeUIDemoModeAppURL[];
+extern const char kChromeUntrustedUIDemoModeAppHost[];
+extern const char kChromeUntrustedUIDemoModeAppURL[];
 
 }  // namespace ash
 
diff --git a/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc
index c91c8e1a5..b56fe9e 100644
--- a/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc
@@ -171,6 +171,9 @@
   }
 
   ~NetworkHealthProviderTest() override {
+    // Clear in process instance prior to destroying cros_network_config_ to
+    // avoid UaF errors.
+    network_config::OverrideInProcessInstanceForTesting(nullptr);
     // Ordering here is based on dependencies between classes,
     // CrosNetworkConfig depends on NetworkHandler and NetworkHandler
     // indirectly depends on NetworkCertLoader.
diff --git a/ash/webui/diagnostics_ui/backend/system_data_provider.cc b/ash/webui/diagnostics_ui/backend/system_data_provider.cc
index b7f23f6..b842fb5 100644
--- a/ash/webui/diagnostics_ui/backend/system_data_provider.cc
+++ b/ash/webui/diagnostics_ui/backend/system_data_provider.cc
@@ -56,14 +56,21 @@
 void PopulateCpuInfo(const healthd::CpuInfo& cpu_info,
                      mojom::SystemInfo& out_system_info) {
   const PhysicalCpuInfos& physical_cpus = cpu_info.physical_cpus;
-  DCHECK_GE(physical_cpus.size(), 1u);
-
   out_system_info.cpu_threads_count = cpu_info.num_total_threads;
 
+  if (physical_cpus.empty()) {
+    LOG(ERROR) << "No physical cpus in SystemInfo response.";
+    return;
+  }
+
   // If there is more than one physical cpu on the device, use the name of the
   // first CPU.
   out_system_info.cpu_model_name = physical_cpus[0]->model_name.value_or("");
 
+  if (physical_cpus[0]->logical_cpus.empty()) {
+    LOG(ERROR) << "Device reported having 0 logical CPUs.";
+    return;
+  }
   // Calculate `max_clock_speed_khz` as the average of all logical core clock
   // speeds until we decide the best way to consume the information in the UI.
   uint32_t total_max_ghz = 0;
@@ -204,6 +211,11 @@
 
 void PopulateAverageCpuTemperature(const healthd::CpuInfo& cpu_info,
                                    mojom::CpuUsage& out_cpu_usage) {
+  if (cpu_info.temperature_channels.empty()) {
+    LOG(ERROR) << "Device reported having 0 temperature channels.";
+    return;
+  }
+
   uint32_t cumulative_total = 0;
   for (const auto& temp_channel_ptr : cpu_info.temperature_channels) {
     cumulative_total += temp_channel_ptr->temperature_celsius;
@@ -216,6 +228,12 @@
 
 void PopulateAverageScaledClockSpeed(const healthd::CpuInfo& cpu_info,
                                      mojom::CpuUsage& out_cpu_usage) {
+  if (cpu_info.physical_cpus.empty() ||
+      cpu_info.physical_cpus[0]->logical_cpus.empty()) {
+    LOG(ERROR) << "Device reported having 0 logical CPUs.";
+    return;
+  }
+
   uint32_t total_scaled_ghz = 0;
   for (const auto& logical_cpu_ptr : cpu_info.physical_cpus[0]->logical_cpus) {
     total_scaled_ghz += logical_cpu_ptr->scaling_current_frequency_khz;
@@ -554,6 +572,8 @@
     return;
   }
 
+  // TODO(ashleydp): Add metrics to track the occurrence of invalid cros_healthd
+  // CpuInfo responses.
   const healthd::CpuInfo* cpu_info = GetCpuInfo(*info_ptr);
   if (cpu_info == nullptr) {
     LOG(ERROR) << "No CpuInfo in response from cros_healthd.";
@@ -571,6 +591,16 @@
 void SystemDataProvider::ComputeAndPopulateCpuUsage(
     const healthd::CpuInfo& cpu_info,
     mojom::CpuUsage& out_cpu_usage) {
+  if (cpu_info.physical_cpus.empty()) {
+    LOG(ERROR) << "Device reported having zero physical CPUs";
+    return;
+  }
+
+  if (cpu_info.physical_cpus[0]->logical_cpus.empty()) {
+    LOG(ERROR) << "Device reported having zero logical CPUs";
+    return;
+  }
+
   // For simplicity, assume that all devices have just one physical CPU, made
   // up of one or more virtual CPUs.
 
diff --git a/ash/webui/diagnostics_ui/backend/system_data_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/system_data_provider_unittest.cc
index 1f783be..8129406 100644
--- a/ash/webui/diagnostics_ui/backend/system_data_provider_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/system_data_provider_unittest.cc
@@ -1091,5 +1091,64 @@
   EXPECT_FALSE(isnan(battery_health_three->charge_full_design_milliamp_hours));
 }
 
+TEST_F(SystemDataProviderTest, CpuUsagePtrDataValidation) {
+  // Setup Timer
+  auto timer = std::make_unique<base::MockRepeatingTimer>();
+  auto* timer_ptr = timer.get();
+  system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
+
+  FakeCpuUsageObserver cpu_usage_observer;
+  system_data_provider_->ObserveCpuUsage(
+      cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
+
+  // Simulate receiving a nullptr for CpuInfo.
+  SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr, nullptr,
+                                /*memory_info=*/nullptr,
+                                /*system_info=*/nullptr);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1u, cpu_usage_observer.updates.size());
+  EXPECT_EQ(0u, cpu_usage_observer.updates[0]->average_cpu_temp_celsius);
+  EXPECT_EQ(0u, cpu_usage_observer.updates[0]->scaling_current_frequency_khz);
+
+  // Simulate receiving a CpuInfo with no data set.
+  healthd_mojom::CpuInfoPtr cpu_info_no_data = healthd_mojom::CpuInfo::New();
+  SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
+                                std::move(cpu_info_no_data),
+                                /*memory_info=*/nullptr,
+                                /*system_info=*/nullptr);
+
+  // Trigger timer to update data.
+  timer_ptr->Fire();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2u, cpu_usage_observer.updates.size());
+  EXPECT_EQ(0u, cpu_usage_observer.updates[1]->average_cpu_temp_celsius);
+  EXPECT_EQ(0u, cpu_usage_observer.updates[1]->scaling_current_frequency_khz);
+
+  // Simulate receiving a CpuInfo with and empty temperature channel and empty
+  // logical cpu.
+  std::vector<healthd_mojom::PhysicalCpuInfoPtr> physical_cpus;
+  physical_cpus.emplace_back(healthd_mojom::PhysicalCpuInfo::New());
+  std::vector<healthd_mojom::CpuTemperatureChannelPtr> temperature_channels;
+  healthd_mojom::CpuInfoPtr cpu_info_no_temperatures =
+      healthd_mojom::CpuInfo::New(/*num_total_threads=*/0u,
+                                  healthd_mojom::CpuArchitectureEnum::kUnknown,
+                                  std::move(physical_cpus),
+                                  std::move(temperature_channels), nullptr);
+  SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
+                                std::move(cpu_info_no_temperatures),
+                                /*memory_info=*/nullptr,
+                                /*system_info=*/nullptr);
+
+  // Trigger timer to update data.
+  timer_ptr->Fire();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(3u, cpu_usage_observer.updates.size());
+  EXPECT_EQ(0u, cpu_usage_observer.updates[2]->average_cpu_temp_celsius);
+  EXPECT_EQ(0u, cpu_usage_observer.updates[2]->scaling_current_frequency_khz);
+}
+
 }  // namespace diagnostics
 }  // namespace ash
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.html b/ash/webui/os_feedback_ui/resources/feedback_flow.html
index f84ff78a..2f740756 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.html
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.html
@@ -1,6 +1,7 @@
 <div>
   <iron-pages attr-for-selected="id" selected="[[currentState_]]">
-    <search-page id="searchPage" on-continue-click="handleContinueClick_">
+    <search-page id="searchPage" on-continue-click="handleContinueClick_"
+        description-template="[[descriptionTemplate_]]">
     </search-page>
     <share-data-page id="shareDataPage" feedback-context="[[feedbackContext_]]"
         on-continue-click="handleContinueClick_"
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.js b/ash/webui/os_feedback_ui/resources/feedback_flow.js
index 94e89cc41..edb492b 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.js
@@ -30,6 +30,7 @@
  * @enum {string}
  */
 export const AdditionalContextQueryParam = {
+  DESCRIPTION_TEMPLATE: 'description_template',
   EXTRA_DIAGNOSTICS: 'extra_diagnostics',
 };
 
@@ -81,6 +82,14 @@
     this.description_;
 
     /**
+     * The description template provided source application to help user write
+     * feedback.
+     * @type {string}
+     * @protected
+     */
+    this.descriptionTemplate_;
+
+    /**
      * The status of sending report.
      * @type {?SendReportStatus}
      * @private
@@ -126,6 +135,12 @@
         params.get(AdditionalContextQueryParam.EXTRA_DIAGNOSTICS);
     this.feedbackContext_.extraDiagnostics =
         extraDiagnostics ? decodeURIComponent(extraDiagnostics) : '';
+    const descriptionTemplate =
+        params.get(AdditionalContextQueryParam.DESCRIPTION_TEMPLATE);
+    this.descriptionTemplate_ =
+        descriptionTemplate && descriptionTemplate.length > 0 ?
+        decodeURIComponent(descriptionTemplate) :
+        '';
   }
 
   /**
diff --git a/ash/webui/os_feedback_ui/resources/search_page.js b/ash/webui/os_feedback_ui/resources/search_page.js
index aecc1ee6..7b20d8c 100644
--- a/ash/webui/os_feedback_ui/resources/search_page.js
+++ b/ash/webui/os_feedback_ui/resources/search_page.js
@@ -57,9 +57,22 @@
     return html`{__html_template__}`;
   }
 
+  static get properties() {
+    return {
+      descriptionTemplate: {
+        type: String,
+        readonly: true,
+        observer: SearchPageElement.prototype.descriptionTemplateChanged_
+      },
+    };
+  }
+
   constructor() {
     super();
 
+    /** @type {string} */
+    this.descriptionTemplate = '';
+
     /**
      * Record the most recent number of characters in the input for which a
      * search has been attempted.
@@ -268,6 +281,14 @@
   setDescription(text) {
     this.getInputElement_().value = text;
   }
+
+  /**
+   * @param {string} currentTemplate
+   * @protected
+   */
+  descriptionTemplateChanged_(currentTemplate) {
+    this.getInputElement_().value = currentTemplate;
+  }
 }
 
 customElements.define(SearchPageElement.is, SearchPageElement);
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index 47e4a43..4cae652 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -62,7 +62,7 @@
 // Gets the next non-zero identifier. It is only unique within a process.
 uint32_t GetNextDataId() {
   uint32_t id;
-  while ((id = static_cast<uint32_t>(g_next_id.GetNext())) == 0) {
+  while ((id = g_next_id.GetNext()) == 0) {
   }
   return id;
 }
@@ -394,9 +394,9 @@
       } break;
       case BOOL_VALUE:
       case CHAR_VALUE:
-        value.short_value_ = static_cast<uint64_t>(
+        value.short_value_ =
             reinterpret_cast<std::atomic<char>*>(entry.second.memory.get())
-                ->load(std::memory_order_relaxed));
+                ->load(std::memory_order_relaxed);
         break;
       case SIGNED_VALUE:
       case UNSIGNED_VALUE:
@@ -450,12 +450,18 @@
                             ValueType type,
                             const void* memory,
                             size_t size) {
-  DCHECK_LT(name.length(), kMaxUserDataNameLength);
+  DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length());
+  size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1),
+                  size);
 
   // It's possible that no user data is being stored.
   if (!memory_)
     return nullptr;
 
+  // The storage of a name is limited so use that limit during lookup.
+  if (name.length() > kMaxUserDataNameLength)
+    name = StringPiece(name.data(), kMaxUserDataNameLength);
+
   ValueInfo* info;
   auto existing = values_.find(name);
   if (existing != values_.end()) {
@@ -478,18 +484,12 @@
     if (base_size > available_)
       return nullptr;
 
-    // The "full size" is the size for storing the entire value.  This must fit
-    // into a uint16_t.
-    size_t full_size =
-        std::min({base_size + value_extent, available_,
-                  bits::AlignDown(std::numeric_limits<uint16_t>::max(),
-                                  kMemoryAlignment)});
+    // The "full size" is the size for storing the entire value.
+    size_t full_size = std::min(base_size + value_extent, available_);
 
     // If the value is actually a single byte, see if it can be stuffed at the
     // end of the name extent rather than wasting kMemoryAlignment bytes.
     if (size == 1 && name_extent > name_size) {
-      // This assignment is safe because `base_size` cannot be much larger than
-      // UINT8_MAX.
       full_size = base_size;
       --name_extent;
       --base_size;
@@ -513,7 +513,7 @@
     DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed));
     DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed));
     header->name_size = static_cast<uint8_t>(name_size);
-    header->record_size = static_cast<uint16_t>(full_size);
+    header->record_size = full_size;
     char* name_memory = reinterpret_cast<char*>(header) + sizeof(FieldHeader);
     void* value_memory =
         reinterpret_cast<char*>(header) + sizeof(FieldHeader) + name_extent;
@@ -541,9 +541,7 @@
   size = std::min(size, info->extent);
   info->size_ptr->store(0, std::memory_order_seq_cst);
   memcpy(info->memory, memory, size);
-  // This cast is safe because `size` <= info->extent < `full_size`, and
-  // `full_size` fits in a uint16_t.
-  info->size_ptr->store(static_cast<uint16_t>(size), std::memory_order_release);
+  info->size_ptr->store(size, std::memory_order_release);
 
   // The address of the stored value is returned so it can be re-used by the
   // caller, so long as it's done in an atomic way.
@@ -1164,7 +1162,7 @@
   record->age = info.age;
   memcpy(record->identifier, info.identifier, sizeof(identifier));
   memcpy(record->pickle, pickler.data(), pickler.size());
-  record->pickle_size = checked_cast<uint16_t>(pickler.size());
+  record->pickle_size = pickler.size();
   record->changes.store(0, std::memory_order_relaxed);
 
   // Initialize the owner info.
@@ -1741,13 +1739,14 @@
                     ActivityData::ForGeneric(id_, info));
 }
 
-ScopedTaskRunActivity::ScopedTaskRunActivity(const void* program_counter,
-                                             const base::PendingTask& task)
+ScopedTaskRunActivity::ScopedTaskRunActivity(
+    const void* program_counter,
+    const base::PendingTask& task)
     : GlobalActivityTracker::ScopedThreadActivity(
           program_counter,
           task.posted_from.program_counter(),
           Activity::ACT_TASK_RUN,
-          ActivityData::ForTask(static_cast<uint64_t>(task.sequence_num)),
+          ActivityData::ForTask(task.sequence_num),
           /*lock_allowed=*/true) {}
 
 ScopedLockAcquireActivity::ScopedLockAcquireActivity(
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index 1199cae..b5da6d9f 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -1137,7 +1137,7 @@
 
     OwningProcess owner;            // The process that created this record.
     uint64_t address;               // The base address of the module.
-    int64_t load_time;              // Time of last load/unload.
+    uint64_t load_time;             // Time of last load/unload.
     uint64_t size;                  // The size of the module in bytes.
     uint32_t timestamp;             // Opaque timestamp of the module.
     uint32_t age;                   // Opaque "age" associated with the module.
diff --git a/base/guid.cc b/base/guid.cc
index 48d147de..9c16d3a 100644
--- a/base/guid.cc
+++ b/base/guid.cc
@@ -48,7 +48,7 @@
     } else {
       if (strict ? !IsLowerHexDigit(current) : !IsHexDigit(current))
         return std::string();
-      lowercase_[i] = static_cast<char>(ToLowerASCII(current));
+      lowercase_[i] = ToLowerASCII(current);
     }
   }
 
diff --git a/base/logging.cc b/base/logging.cc
index 99c7556f..ad63ada 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -661,7 +661,7 @@
 }
 
 LogMessage::~LogMessage() {
-  size_t stack_start = stream_.str().length();
+  size_t stack_start = stream_.tellp();
 #if !defined(OFFICIAL_BUILD) && !BUILDFLAG(IS_NACL) && !defined(__UCLIBC__) && \
     !BUILDFLAG(IS_AIX)
   if (severity_ == LOGGING_FATAL && !base::debug::BeingDebugged()) {
diff --git a/base/memory/platform_shared_memory_mapper_win.cc b/base/memory/platform_shared_memory_mapper_win.cc
index 92808ec8..6f4a4b3 100644
--- a/base/memory/platform_shared_memory_mapper_win.cc
+++ b/base/memory/platform_shared_memory_mapper_win.cc
@@ -18,8 +18,8 @@
   if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
     return 0;
   return memory_info.RegionSize -
-         static_cast<size_t>(static_cast<char*>(address) -
-                             static_cast<char*>(memory_info.AllocationBase));
+         (static_cast<char*>(address) -
+          static_cast<char*>(memory_info.AllocationBase));
 }
 }  // namespace
 
diff --git a/base/memory/shared_memory_mapping.cc b/base/memory/shared_memory_mapping.cc
index 82c03f3..6afcf2fb3 100644
--- a/base/memory/shared_memory_mapping.cc
+++ b/base/memory/shared_memory_mapping.cc
@@ -65,8 +65,7 @@
   uint8_t* aligned_data =
       bits::AlignDown(mapped_span_.data(), SysInfo::VMAllocationGranularity());
   size_t adjusted_size =
-      mapped_span_.size() +
-      static_cast<size_t>(mapped_span_.data() - aligned_data);
+      mapped_span_.size() + (mapped_span_.data() - aligned_data);
   span<uint8_t> span_to_unmap = make_span(aligned_data, adjusted_size);
   mapper->Unmap(span_to_unmap);
 }
diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm
index 7be66d6..f927e86 100644
--- a/base/message_loop/message_pump_mac.mm
+++ b/base/message_loop/message_pump_mac.mm
@@ -430,12 +430,12 @@
   // The timer fired, assume we have work and let RunWork() figure out what to
   // do and what to schedule after.
   base::mac::CallWithEHFrame(^{
-#if !BUILDFLAG(IS_IOS)
-    // TODO(crbug.com/1338267): Attempt to re-enable this DCHECK on iOS after
-    // migrating base::TimeTicks::Now() to
-    // clock_gettime_nsec_np(CLOCK_UPTIME_RAW).
-    DCHECK_GE(base::TimeTicks::Now(), self->delayed_work_scheduled_at_);
-#endif
+    // It would be incorrect to expect that `self->delayed_work_scheduled_at_`
+    // is smaller than or equal to `TimeTicks::Now()` because the fire date of a
+    // CFRunLoopTimer can be adjusted slightly.
+    // https://developer.apple.com/documentation/corefoundation/1543570-cfrunlooptimercreate?language=objc
+    DCHECK(!self->delayed_work_scheduled_at_.is_max());
+
     self->delayed_work_scheduled_at_ = base::TimeTicks::Max();
     self->RunWork();
   });
diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc
index 2691a8f..583d05f8 100644
--- a/base/metrics/persistent_memory_allocator.cc
+++ b/base/metrics/persistent_memory_allocator.cc
@@ -704,16 +704,14 @@
     // Don't leave a slice at the end of a page too small for anything. This
     // can result in an allocation up to two alignment-sizes greater than the
     // minimum required by requested-size + header + alignment.
-    if (page_free - size < sizeof(BlockHeader) + kAllocAlignment) {
+    if (page_free - size < sizeof(BlockHeader) + kAllocAlignment)
       size = page_free;
-      if (freeptr + size > mem_size_) {
-        SetCorrupt();
-        return kReferenceNull;
-      }
-    }
 
-    // This cast is safe because (freeptr + size) <= mem_size_.
-    const uint32_t new_freeptr = static_cast<uint32_t>(freeptr + size);
+    const uint32_t new_freeptr = freeptr + size;
+    if (new_freeptr > mem_size_) {
+      SetCorrupt();
+      return kReferenceNull;
+    }
 
     // Save our work. Try again if another thread has completed an allocation
     // while we were processing. A "weak" exchange would be permissable here
diff --git a/base/pickle.cc b/base/pickle.cc
index 5d7c732..e7334714 100644
--- a/base/pickle.cc
+++ b/base/pickle.cc
@@ -16,7 +16,7 @@
 namespace base {
 
 // static
-const size_t Pickle::kPayloadUnit = 64;
+const int Pickle::kPayloadUnit = 64;
 
 static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
 
@@ -57,8 +57,9 @@
   return current_read_ptr;
 }
 
-const char* PickleIterator::GetReadPointerAndAdvance(size_t num_bytes) {
-  if (num_bytes > end_index_ - read_index_) {
+const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) {
+  if (num_bytes < 0 ||
+      end_index_ - read_index_ < static_cast<size_t>(num_bytes)) {
     read_index_ = end_index_;
     return nullptr;
   }
@@ -68,10 +69,10 @@
 }
 
 inline const char* PickleIterator::GetReadPointerAndAdvance(
-    size_t num_elements,
+    int num_elements,
     size_t size_element) {
-  // Check for size_t overflow.
-  size_t num_bytes;
+  // Check for int32_t overflow.
+  int num_bytes;
   if (!CheckMul(num_elements, size_element).AssignIfValid(&num_bytes))
     return nullptr;
   return GetReadPointerAndAdvance(num_bytes);
@@ -138,8 +139,8 @@
 }
 
 bool PickleIterator::ReadString(std::string* result) {
-  size_t len;
-  if (!ReadLength(&len))
+  int len;
+  if (!ReadInt(&len))
     return false;
   const char* read_from = GetReadPointerAndAdvance(len);
   if (!read_from)
@@ -150,8 +151,8 @@
 }
 
 bool PickleIterator::ReadStringPiece(StringPiece* result) {
-  size_t len;
-  if (!ReadLength(&len))
+  int len;
+  if (!ReadInt(&len))
     return false;
   const char* read_from = GetReadPointerAndAdvance(len);
   if (!read_from)
@@ -162,8 +163,8 @@
 }
 
 bool PickleIterator::ReadString16(std::u16string* result) {
-  size_t len;
-  if (!ReadLength(&len))
+  int len;
+  if (!ReadInt(&len))
     return false;
   const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16_t));
   if (!read_from)
@@ -174,8 +175,8 @@
 }
 
 bool PickleIterator::ReadStringPiece16(StringPiece16* result) {
-  size_t len;
-  if (!ReadLength(&len))
+  int len;
+  if (!ReadInt(&len))
     return false;
   const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16_t));
   if (!read_from)
@@ -185,11 +186,11 @@
   return true;
 }
 
-bool PickleIterator::ReadData(const char** data, size_t* length) {
+bool PickleIterator::ReadData(const char** data, int* length) {
   *length = 0;
   *data = nullptr;
 
-  if (!ReadLength(length))
+  if (!ReadInt(length))
     return false;
 
   return ReadBytes(data, *length);
@@ -197,7 +198,7 @@
 
 bool PickleIterator::ReadData(base::span<const uint8_t>* data) {
   const char* ptr;
-  size_t length;
+  int length;
 
   if (!ReadData(&ptr, &length))
     return false;
@@ -206,7 +207,7 @@
   return true;
 }
 
-bool PickleIterator::ReadBytes(const char** data, size_t length) {
+bool PickleIterator::ReadBytes(const char** data, int length) {
   const char* read_from = GetReadPointerAndAdvance(length);
   if (!read_from)
     return false;
@@ -231,12 +232,12 @@
   header_->payload_size = 0;
 }
 
-Pickle::Pickle(size_t header_size)
+Pickle::Pickle(int header_size)
     : header_(nullptr),
       header_size_(bits::AlignUp(header_size, sizeof(uint32_t))),
       capacity_after_header_(0),
       write_offset_(0) {
-  DCHECK_GE(header_size, sizeof(Header));
+  DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
   DCHECK_LE(header_size, kPayloadUnit);
   Resize(kPayloadUnit);
   header_->payload_size = 0;
@@ -247,10 +248,10 @@
       header_size_(0),
       capacity_after_header_(kCapacityReadOnly),
       write_offset_(0) {
-  if (data_len >= sizeof(Header))
+  if (data_len >= static_cast<int>(sizeof(Header)))
     header_size_ = data_len - header_->payload_size;
 
-  if (header_size_ > data_len)
+  if (header_size_ > static_cast<unsigned int>(data_len))
     header_size_ = 0;
 
   if (header_size_ != bits::AlignUp(header_size_, sizeof(uint32_t)))
@@ -300,20 +301,22 @@
 }
 
 void Pickle::WriteString(const StringPiece& value) {
-  WriteData(value.data(), value.size());
+  WriteInt(static_cast<int>(value.size()));
+  WriteBytes(value.data(), static_cast<int>(value.size()));
 }
 
 void Pickle::WriteString16(const StringPiece16& value) {
-  WriteInt(checked_cast<int>(value.size()));
-  WriteBytes(value.data(), value.size() * sizeof(char16_t));
+  WriteInt(static_cast<int>(value.size()));
+  WriteBytes(value.data(), static_cast<int>(value.size()) * sizeof(char16_t));
 }
 
-void Pickle::WriteData(const char* data, size_t length) {
-  WriteInt(checked_cast<int>(length));
+void Pickle::WriteData(const char* data, int length) {
+  DCHECK_GE(length, 0);
+  WriteInt(length);
   WriteBytes(data, length);
 }
 
-void Pickle::WriteBytes(const void* data, size_t length) {
+void Pickle::WriteBytes(const void* data, int length) {
   WriteBytesCommon(data, length);
 }
 
diff --git a/base/pickle.h b/base/pickle.h
index e719fcf..5c80e87f 100644
--- a/base/pickle.h
+++ b/base/pickle.h
@@ -54,7 +54,7 @@
   // placed in |*length|. The pointer placed into |*data| points into the
   // message's buffer so it will be scoped to the lifetime of the message (or
   // until the message data is mutated). Do not keep the pointer around!
-  [[nodiscard]] bool ReadData(const char** data, size_t* length);
+  [[nodiscard]] bool ReadData(const char** data, int* length);
 
   // Similar, but using base::span for convenience.
   [[nodiscard]] bool ReadData(base::span<const uint8_t>* data);
@@ -64,21 +64,17 @@
   // pointer placed into |*data| points into the message's buffer so it will be
   // scoped to the lifetime of the message (or until the message data is
   // mutated). Do not keep the pointer around!
-  [[nodiscard]] bool ReadBytes(const char** data, size_t length);
+  [[nodiscard]] bool ReadBytes(const char** data, int length);
 
-  // A version of ReadInt() that checks for the result not being negative. Use
-  // it for reading the object sizes.
-  [[nodiscard]] bool ReadLength(size_t* result) {
-    int result_int;
-    if (!ReadInt(&result_int) || result_int < 0)
-      return false;
-    *result = static_cast<size_t>(result_int);
-    return true;
+  // A safer version of ReadInt() that checks for the result not being negative.
+  // Use it for reading the object sizes.
+  [[nodiscard]] bool ReadLength(int* result) {
+    return ReadInt(result) && *result >= 0;
   }
 
   // Skips bytes in the read buffer and returns true if there are at least
   // num_bytes available. Otherwise, does nothing and returns false.
-  [[nodiscard]] bool SkipBytes(size_t num_bytes) {
+  [[nodiscard]] bool SkipBytes(int num_bytes) {
     return !!GetReadPointerAndAdvance(num_bytes);
   }
 
@@ -98,12 +94,12 @@
   const char* GetReadPointerAndAdvance();
 
   // Get read pointer for |num_bytes| and advance read pointer. This method
-  // checks num_bytes for wrapping.
-  const char* GetReadPointerAndAdvance(size_t num_bytes);
+  // checks num_bytes for negativity and wrapping.
+  const char* GetReadPointerAndAdvance(int num_bytes);
 
   // Get read pointer for (num_elements * size_element) bytes and advance read
-  // pointer. This method checks for overflow and wrapping.
-  const char* GetReadPointerAndAdvance(size_t num_elements,
+  // pointer. This method checks for int overflow, negativity and wrapping.
+  const char* GetReadPointerAndAdvance(int num_elements,
                                        size_t size_element);
 
   const char* payload_;  // Start of our pickle's payload.
@@ -153,7 +149,7 @@
   // Initialize a Pickle object with the specified header size in bytes, which
   // must be greater-than-or-equal-to sizeof(Pickle::Header).  The header size
   // will be rounded up to ensure that the header size is 32bit-aligned.
-  explicit Pickle(size_t header_size);
+  explicit Pickle(int header_size);
 
   // Initializes a Pickle from a const block of data.  The data is not copied;
   // instead the data is merely referenced by this Pickle.  Only const methods
@@ -210,11 +206,11 @@
   void WriteString16(const StringPiece16& value);
   // "Data" is a blob with a length. When you read it out you will be given the
   // length. See also WriteBytes.
-  void WriteData(const char* data, size_t length);
+  void WriteData(const char* data, int length);
   // "Bytes" is a blob with no length. The caller must specify the length both
   // when reading and writing. It is normally used to serialize PoD types of a
   // known size. See also WriteData.
-  void WriteBytes(const void* data, size_t length);
+  void WriteBytes(const void* data, int length);
 
   // WriteAttachment appends |attachment| to the pickle. It returns
   // false iff the set is full or if the Pickle implementation does not support
@@ -312,7 +308,7 @@
                        size_t* pickle_size);
 
   // The allocation granularity of the payload.
-  static const size_t kPayloadUnit;
+  static const int kPayloadUnit;
 
  private:
   friend class PickleIterator;
diff --git a/base/pickle_fuzzer.cc b/base/pickle_fuzzer.cc
index 91970bf..abb7f0f 100644
--- a/base/pickle_fuzzer.cc
+++ b/base/pickle_fuzzer.cc
@@ -98,7 +98,7 @@
       }
       case 13: {
         const char* data_result = nullptr;
-        size_t length_result = 0;
+        int length_result = 0;
         std::ignore = iter.ReadData(&data_result, &length_result);
         break;
       }
@@ -106,18 +106,17 @@
         const char* data_result = nullptr;
         int read_length =
             data_provider.ConsumeIntegralInRange(0, kMaxReadLength);
-        std::ignore =
-            iter.ReadBytes(&data_result, static_cast<size_t>(read_length));
+        std::ignore = iter.ReadBytes(&data_result, read_length);
         break;
       }
       case 15: {
-        size_t result = 0;
+        int result = 0;
         std::ignore = iter.ReadLength(&result);
         break;
       }
       case 16: {
-        std::ignore = iter.SkipBytes(static_cast<size_t>(
-            data_provider.ConsumeIntegralInRange(0, kMaxSkipBytes)));
+        std::ignore = iter.SkipBytes(
+            data_provider.ConsumeIntegralInRange(0, kMaxSkipBytes));
         break;
       }
     }
diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc
index 926193b2..322f163e 100644
--- a/base/pickle_unittest.cc
+++ b/base/pickle_unittest.cc
@@ -37,7 +37,7 @@
 // Test raw char16_t writing, assumes UTF16 encoding is ANSI for alpha chars.
 const char16_t testrawstring16[] = {'A', 'l', 'o', 'h', 'a', 0};
 const char testdata[] = "AAA\0BBB\0";
-const size_t testdatalen = std::size(testdata) - 1;
+const int testdatalen = std::size(testdata) - 1;
 
 // checks that the results can be read correctly from the Pickle
 void VerifyResult(const Pickle& pickle) {
@@ -98,7 +98,7 @@
   EXPECT_EQ(testrawstring16, outstringpiece16);
 
   const char* outdata;
-  size_t outdatalen;
+  int outdatalen;
   EXPECT_TRUE(iter.ReadData(&outdata, &outdatalen));
   EXPECT_EQ(testdatalen, outdatalen);
   EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
@@ -442,7 +442,8 @@
   // note that any data will have a 4-byte header indicating the size
   const size_t payload_size_after_header = unit - sizeof(uint32_t);
   Pickle pickle;
-  pickle.WriteData(data_ptr, payload_size_after_header - sizeof(uint32_t));
+  pickle.WriteData(
+      data_ptr, static_cast<int>(payload_size_after_header - sizeof(uint32_t)));
   size_t cur_payload = payload_size_after_header;
 
   // note: we assume 'unit' is a power of 2
@@ -450,7 +451,7 @@
   EXPECT_EQ(pickle.payload_size(), payload_size_after_header);
 
   // fill out a full page (noting data header)
-  pickle.WriteData(data_ptr, unit - sizeof(uint32_t));
+  pickle.WriteData(data_ptr, static_cast<int>(unit - sizeof(uint32_t)));
   cur_payload += unit;
   EXPECT_EQ(unit * 2, pickle.capacity_after_header());
   EXPECT_EQ(cur_payload, pickle.payload_size());
@@ -530,9 +531,9 @@
 
   PickleIterator iter(pickle);
   const char* outdata;
-  size_t outdatalen;
+  int outdatalen;
   EXPECT_TRUE(iter.ReadData(&outdata, &outdatalen));
-  EXPECT_EQ(0u, outdatalen);
+  EXPECT_EQ(0, outdatalen);
   // We can't assert that outdata is NULL.
 }
 
diff --git a/base/power_monitor/moving_average.cc b/base/power_monitor/moving_average.cc
index 2f21b73..bac4f770 100644
--- a/base/power_monitor/moving_average.cc
+++ b/base/power_monitor/moving_average.cc
@@ -8,7 +8,6 @@
 #include <limits>
 
 #include "base/check_op.h"
-#include "base/numerics/clamped_math.h"
 
 namespace {
 constexpr int kIntMax = std::numeric_limits<int>::max();
@@ -35,17 +34,15 @@
 }
 
 int MovingAverage::GetAverageRoundedDown() const {
-  if (Size() == 0 || uint64_t{Size()} > static_cast<uint64_t>(kInt64Max)) {
+  if (Size() == 0)
     return 0;
-  }
-  return static_cast<int>(sum_ / static_cast<int64_t>(Size()));
+  return sum_ / Size();
 }
 
 int MovingAverage::GetAverageRoundedToClosest() const {
-  if (Size() == 0 || uint64_t{Size()} > static_cast<uint64_t>(kInt64Max))
+  if (Size() == 0)
     return 0;
-  return static_cast<int>((base::ClampedNumeric<int64_t>(sum_) + Size() / 2) /
-                          static_cast<int64_t>(Size()));
+  return (sum_ + Size() / 2) / Size();
 }
 
 double MovingAverage::GetUnroundedAverage() const {
diff --git a/base/power_monitor/speed_limit_observer_win.cc b/base/power_monitor/speed_limit_observer_win.cc
index 935546ea..139f7e8 100644
--- a/base/power_monitor/speed_limit_observer_win.cc
+++ b/base/power_monitor/speed_limit_observer_win.cc
@@ -72,7 +72,7 @@
 SpeedLimitObserverWin::SpeedLimitObserverWin(
     SpeedLimitUpdateCallback speed_limit_update_callback)
     : callback_(std::move(speed_limit_update_callback)),
-      num_cpus_(static_cast<size_t>(SysInfo::NumberOfProcessors())),
+      num_cpus_(SysInfo::NumberOfProcessors()),
       moving_average_(kMovingAverageWindowSize) {
   DVLOG(1) << __func__ << "(num_CPUs=" << num_cpus() << ")";
   timer_.Start(FROM_HERE, kSampleInterval, this,
@@ -147,8 +147,7 @@
   std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpus());
   if (!NT_SUCCESS(CallNtPowerInformation(
           ProcessorInformation, nullptr, 0, &info[0],
-          static_cast<ULONG>(sizeof(PROCESSOR_POWER_INFORMATION) *
-                             num_cpus())))) {
+          sizeof(PROCESSOR_POWER_INFORMATION) * num_cpus()))) {
     return throttling_level;
   }
 
@@ -163,9 +162,9 @@
   // any type of throttling (thermal, power-limit, PMAX etc) starts.
   int num_non_idle_cpus = 0;
   float load_fraction_total = 0.0;
-  for (size_t i = 0; i < num_cpus(); ++i) {
+  for (int i = 0; i < num_cpus(); ++i) {
     // Amount of "non-idleness" is the distance from the max idle state.
-    const auto idle_diff = info[i].MaxIdleState - info[i].CurrentIdleState;
+    const int idle_diff = info[i].MaxIdleState - info[i].CurrentIdleState;
     // Derive a value between 0.0 and 1.0 where 1.0 corresponds to max load on
     // CPU#i.
     // Example: MaxIdleState=2, CurrentIdleState=1 => (2 - 1) / 2 = 0.5.
diff --git a/base/power_monitor/speed_limit_observer_win.h b/base/power_monitor/speed_limit_observer_win.h
index cd5868e..3e95b33 100644
--- a/base/power_monitor/speed_limit_observer_win.h
+++ b/base/power_monitor/speed_limit_observer_win.h
@@ -38,7 +38,7 @@
   void OnTimerTick();
   float EstimateThrottlingLevel();
 
-  size_t num_cpus() const { return num_cpus_; }
+  int num_cpus() const { return num_cpus_; }
 
   const SpeedLimitUpdateCallback callback_;
 
@@ -48,7 +48,7 @@
   // Number of logical cores in the existing physical processor.
   // Example: a processor with 6 cores which supports hyperthreading has 12
   // logical cores, hence `num_cpus_` equals 12 in this case.
-  const size_t num_cpus_;
+  const int num_cpus_;
   // A simple MA filter of size 10 is used to smooth out the speed-limit
   // value and to remove noise from short spikes in CPU load. The existing
   // sample rate is one sample per seconds but the existing choice is rather
diff --git a/base/rand_util.cc b/base/rand_util.cc
index 55df30aa..725e1ffd 100644
--- a/base/rand_util.cc
+++ b/base/rand_util.cc
@@ -25,7 +25,7 @@
 int RandInt(int min, int max) {
   DCHECK_LE(min, max);
 
-  uint64_t range = static_cast<uint64_t>(max) - static_cast<uint64_t>(min) + 1;
+  uint64_t range = static_cast<uint64_t>(max) - min + 1;
   // |range| is at most UINT_MAX + 1, so the result of RandGenerator(range)
   // is at most UINT_MAX.  Hence it's safe to cast it from uint64_t to int64_t.
   int result =
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
index 7ebabbf..2f8d8933 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -578,9 +578,7 @@
     }
   }
 
-  // This cast is safe because this function is only called with a positive
-  // value of `accumulated_bytes`.
-  size_t samples = static_cast<size_t>(accumulated_bytes) / mean_interval;
+  size_t samples = accumulated_bytes / mean_interval;
   accumulated_bytes %= mean_interval;
 
   do {
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index 5313f98..4c634544 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -168,7 +168,7 @@
   // center around 10M bytes, which would overflow the buckets.
   base::UmaHistogramCounts10M(
       "HeapProfiling.SamplingIntervalKB",
-      static_cast<int>(poisson_allocation_sampler->SamplingInterval() / 1024));
+      poisson_allocation_sampler->SamplingInterval() / 1024);
 
   AutoLock lock(start_stop_mutex_);
   if (!running_sessions_++)
diff --git a/base/substring_set_matcher/substring_set_matcher.cc b/base/substring_set_matcher/substring_set_matcher.cc
index 0362edb..5e1bfee 100644
--- a/base/substring_set_matcher/substring_set_matcher.cc
+++ b/base/substring_set_matcher/substring_set_matcher.cc
@@ -239,8 +239,7 @@
   // Create new nodes if necessary.
   while (i != text_end) {
     tree_.emplace_back();
-    current_node->SetEdge(static_cast<unsigned char>(*i),
-                          static_cast<NodeID>(tree_.size() - 1));
+    current_node->SetEdge(static_cast<unsigned char>(*i), tree_.size() - 1);
     current_node = &tree_.back();
     ++i;
   }
@@ -381,13 +380,13 @@
 SubstringSetMatcher::AhoCorasickNode::GetEdgeNoInline(uint32_t label) const {
   DCHECK(edges_capacity_ != 0);
 #ifdef __SSE2__
-  const __m128i lbl = _mm_set1_epi32(static_cast<int>(label));
+  const __m128i lbl = _mm_set1_epi32(label);
   const __m128i mask = _mm_set1_epi32(0x1ff);
   for (unsigned edge_idx = 0; edge_idx < num_edges(); edge_idx += 4) {
     const __m128i four = _mm_loadu_si128(
         reinterpret_cast<const __m128i*>(&edges_.edges[edge_idx]));
     const __m128i match = _mm_cmpeq_epi32(_mm_and_si128(four, mask), lbl);
-    const uint32_t match_mask = static_cast<uint32_t>(_mm_movemask_epi8(match));
+    const uint32_t match_mask = _mm_movemask_epi8(match);
     if (match_mask != 0) {
       if (match_mask & 0x1u) {
         return edges_.edges[edge_idx].node_id;
@@ -447,9 +446,6 @@
         edges_capacity_ == 0 ? kNumInlineEdges : edges_capacity_;
     unsigned new_capacity = old_capacity * 2;
     DCHECK_EQ(0u, new_capacity % 4);
-    // TODO(pkasting): The header claims this condition holds, but I don't
-    // understand why.  If you do, please comment.
-    DCHECK_LE(new_capacity, kEmptyLabel + 1);
     AhoCorasickEdge* new_edges = new AhoCorasickEdge[new_capacity];
     memcpy(new_edges, edges(), sizeof(AhoCorasickEdge) * old_capacity);
     for (unsigned edge_idx = old_capacity; edge_idx < new_capacity;
@@ -460,9 +456,8 @@
       delete[] edges_.edges;
     }
     edges_.edges = new_edges;
-    // These casts are safe due to the DCHECK above.
-    edges_capacity_ = static_cast<uint16_t>(new_capacity);
-    num_free_edges_ = static_cast<uint8_t>(new_capacity - old_capacity);
+    edges_capacity_ = new_capacity;
+    num_free_edges_ = new_capacity - old_capacity;
   }
 
   // Insert the new edge at the end of our heap storage.
diff --git a/base/sync_socket_win.cc b/base/sync_socket_win.cc
index 11201884..b35f8f3 100644
--- a/base/sync_socket_win.cc
+++ b/base/sync_socket_win.cc
@@ -152,7 +152,7 @@
     if (!operation_ok) {
       if (::GetLastError() == ERROR_IO_PENDING) {
         HANDLE events[] = { io_event->handle(), cancel_event->handle() };
-        const DWORD wait_result = WaitForMultipleObjects(
+        const int wait_result = WaitForMultipleObjects(
             std::size(events), events, FALSE,
             timeout_in_ms == INFINITE
                 ? timeout_in_ms
diff --git a/base/system/sys_info_win.cc b/base/system/sys_info_win.cc
index dd0cc51..fd60b27 100644
--- a/base/system/sys_info_win.cc
+++ b/base/system/sys_info_win.cc
@@ -162,8 +162,8 @@
                                             int32_t* minor_version,
                                             int32_t* bugfix_version) {
   win::OSInfo* os_info = win::OSInfo::GetInstance();
-  *major_version = static_cast<int32_t>(os_info->version_number().major);
-  *minor_version = static_cast<int32_t>(os_info->version_number().minor);
+  *major_version = os_info->version_number().major;
+  *minor_version = os_info->version_number().minor;
   *bugfix_version = 0;
 }
 
diff --git a/base/token.cc b/base/token.cc
index e2db9087..cf46cf0 100644
--- a/base/token.cc
+++ b/base/token.cc
@@ -33,16 +33,16 @@
     return absl::nullopt;
   }
   uint64_t words[2];
-  for (size_t i = 0; i < 2; i++) {
+  for (int i = 0; i < 2; i++) {
     uint64_t word = 0;
     // This j loop is similar to HexStringToUInt64 but we are intentionally
     // strict about case, accepting 'A' but rejecting 'a'.
-    for (size_t j = 0; j < 16; j++) {
+    for (int j = 0; j < 16; j++) {
       const char c = string_representation[(16 * i) + j];
       if (('0' <= c) && (c <= '9')) {
-        word = (word << 4) | static_cast<uint64_t>(c - '0');
+        word = (word << 4) | (c - '0');
       } else if (('A' <= c) && (c <= 'F')) {
-        word = (word << 4) | static_cast<uint64_t>(c - 'A' + 10);
+        word = (word << 4) | (c - 'A' + 10);
       } else {
         return absl::nullopt;
       }
diff --git a/base/trace_event/interned_args_helper.h b/base/trace_event/interned_args_helper.h
index fb34799..950da3a9 100644
--- a/base/trace_event/interned_args_helper.h
+++ b/base/trace_event/interned_args_helper.h
@@ -25,12 +25,12 @@
 struct BASE_EXPORT TraceSourceLocation {
   const char* function_name = nullptr;
   const char* file_name = nullptr;
-  int line_number = 0;
+  size_t line_number = 0;
 
   TraceSourceLocation() = default;
   TraceSourceLocation(const char* function_name,
                       const char* file_name,
-                      int line_number)
+                      size_t line_number)
       : function_name(function_name),
         file_name(file_name),
         line_number(line_number) {}
@@ -40,7 +40,7 @@
   explicit TraceSourceLocation(const base::Location& location)
       : function_name(location.function_name()),
         file_name(location.file_name()),
-        line_number(location.line_number()) {}
+        line_number(static_cast<size_t>(location.line_number())) {}
 
   bool operator==(const TraceSourceLocation& other) const {
     return file_name == other.file_name &&
diff --git a/base/trace_event/traced_value.cc b/base/trace_event/traced_value.cc
index a311aea..ef495654 100644
--- a/base/trace_event/traced_value.cc
+++ b/base/trace_event/traced_value.cc
@@ -153,7 +153,7 @@
 
     BeginDictionary(name);
     pickle_.WriteBytes(pickle_writer->pickle_.payload(),
-                       pickle_writer->pickle_.payload_size());
+                       static_cast<int>(pickle_writer->pickle_.payload_size()));
     EndDictionary();
   }
 
@@ -163,7 +163,7 @@
 
     BeginDictionaryWithCopiedName(name);
     pickle_.WriteBytes(pickle_writer->pickle_.payload(),
-                       pickle_writer->pickle_.payload_size());
+                       static_cast<int>(pickle_writer->pickle_.payload_size()));
     EndDictionary();
   }
 
diff --git a/base/trace_event/typed_macros_internal.cc b/base/trace_event/typed_macros_internal.cc
index 1ec6b3b..886251a 100644
--- a/base/trace_event/typed_macros_internal.cc
+++ b/base/trace_event/typed_macros_internal.cc
@@ -128,7 +128,7 @@
   if (!g_typed_event_callback)
     return base::trace_event::TrackEventHandle();
 
-  const auto thread_id = base::PlatformThread::CurrentId();
+  const int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
   auto* trace_log = base::trace_event::TraceLog::GetInstance();
   DCHECK(trace_log);
 
diff --git a/build/config/fuchsia/test/README.md b/build/config/fuchsia/test/README.md
index 6402a27..a083229 100644
--- a/build/config/fuchsia/test/README.md
+++ b/build/config/fuchsia/test/README.md
@@ -47,11 +47,8 @@
 https://fuchsia.dev/reference/fidl/fuchsia.web#ContextFeatureFlags.
 Any test-specific exceptions are documented for each file.
 
-#### audio_output.shard.test-cml
-Required by tests that need to enable audio output.
-
-#### platform_video_codecs.shard.test-cml
-Required by tests that need accelerated (e.g., hardware) video codecs.
+#### audio_capabilities.test-cmx
+Corresponds to the `AUDIO` flag. Required for enabling audio input and output.
 
 #### network.shard.test-cml
 For tests that need access to network services, including those that access a
diff --git a/build/config/fuchsia/test/audio_output.shard.test-cml b/build/config/fuchsia/test/audio_output.shard.test-cml
deleted file mode 100644
index d46822a..0000000
--- a/build/config/fuchsia/test/audio_output.shard.test-cml
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  use: [
-    {
-      protocol: [
-        "fuchsia.media.Audio",
-        "fuchsia.media.AudioDeviceEnumerator",
-      ]
-    },
-  ],
-}
diff --git a/build/config/fuchsia/test/platform_video_codecs.shard.test-cml b/build/config/fuchsia/test/platform_video_codecs.shard.test-cml
deleted file mode 100644
index 3935e0f..0000000
--- a/build/config/fuchsia/test/platform_video_codecs.shard.test-cml
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  use: [
-    {
-      protocol: [
-        "fuchsia.mediacodec.CodecFactory",
-      ]
-    },
-  ],
-}
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index f7cce30..72659f91 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -10,15 +10,6 @@
 import("//build/toolchain/toolchain.gni")
 import("//build_overrides/build.gni")
 
-declare_args() {
-  # Set to true if an Xcode project is generated for this build. Set this to
-  # false if you do not plan to run `gn gen --ide=xcode` in this directory.
-  # This will speed up the generation at the cost of generating an invalid
-  # Xcode project if `gn gen --ide=xcode` is used. Defaults to true (favor
-  # correctness over speed).
-  ios_set_attributes_for_xcode_project_generation = true
-}
-
 # Constants corresponding to the bundle type identifiers use application,
 # application extension, XCTest and XCUITest targets respectively.
 _ios_xcode_app_bundle_id = "com.apple.product-type.application"
@@ -303,14 +294,6 @@
     _enable_code_signing = invoker.enable_code_signing
   }
 
-  if (!ios_set_attributes_for_xcode_project_generation) {
-    not_needed(invoker,
-               [
-                 "xcode_product_bundle_id",
-                 "xcode_extra_attributes",
-               ])
-  }
-
   create_bundle(_target_name) {
     forward_variables_from(invoker,
                            [
@@ -347,45 +330,25 @@
       public_deps = []
     }
 
-    if (ios_set_attributes_for_xcode_project_generation) {
-      _bundle_identifier = ""
-      if (defined(invoker.xcode_product_bundle_id)) {
-        _bundle_identifier = invoker.xcode_product_bundle_id
-        assert(
-            _bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
-            "$target_name: bundle_identifier does not respect rfc1034: " +
-                _bundle_identifier)
-      }
+    _bundle_identifier = ""
+    if (defined(invoker.xcode_product_bundle_id)) {
+      _bundle_identifier = invoker.xcode_product_bundle_id
+      assert(_bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
+             "$target_name: bundle_identifier does not respect rfc1034: " +
+                 _bundle_identifier)
+    }
 
-      if (_bundle_identifier != "") {
-        _find_provisioning_profile_args = [
-          "find-provisioning-profile",
-          "-b=" + _bundle_identifier,
-        ]
-        foreach(mobileprovision, ios_mobileprovision_files) {
-          _find_provisioning_profile_args +=
-              [ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
-        }
+    xcode_extra_attributes = {
+      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
+      PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
+      CODE_SIGNING_REQUIRED = "NO"
+      CODE_SIGNING_ALLOWED = "NO"
+      CODE_SIGN_IDENTITY = ""
+      DONT_GENERATE_INFOPLIST_FILE = "YES"
 
-        _ios_provisioning_profile_info =
-            exec_script("//build/config/ios/codesign.py",
-                        _find_provisioning_profile_args,
-                        "json")
-      }
-
-      xcode_extra_attributes = {
-        IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
-        if (_bundle_identifier != "") {
-          CODE_SIGN_IDENTITY = "iPhone Developer"
-          DEVELOPMENT_TEAM = _ios_provisioning_profile_info.team_identifier
-          PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
-          PROVISIONING_PROFILE_SPECIFIER = _ios_provisioning_profile_info.name
-        }
-
-        # If invoker has defined extra attributes, they override the defaults.
-        if (defined(invoker.xcode_extra_attributes)) {
-          forward_variables_from(invoker.xcode_extra_attributes, "*")
-        }
+      # If invoker has defined extra attributes, they override the defaults.
+      if (defined(invoker.xcode_extra_attributes)) {
+        forward_variables_from(invoker.xcode_extra_attributes, "*")
       }
     }
 
@@ -1851,53 +1814,32 @@
       bundle_binary_target = ":$_lipo_loadable_module_target"
       bundle_binary_output = _output_name
 
-      if (ios_set_attributes_for_xcode_project_generation) {
-        _find_provisioning_profile_args = [
-          "find-provisioning-profile",
-          "-b=" + _bundle_identifier,
-        ]
-        foreach(mobileprovision, ios_mobileprovision_files) {
-          _find_provisioning_profile_args +=
-              [ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
+      xcode_extra_attributes = {
+        IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
+        PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
+        CODE_SIGNING_REQUIRED = "NO"
+        CODE_SIGNING_ALLOWED = "NO"
+        CODE_SIGN_IDENTITY = ""
+        DONT_GENERATE_INFOPLIST_FILE = "YES"
+
+        # For XCUITest, Xcode requires specifying the host application name
+        # via the TEST_TARGET_NAME attribute.
+        if (invoker.product_type == _ios_xcode_xcuitest_bundle_id) {
+          TEST_TARGET_NAME = invoker.xcode_test_application_name
         }
 
-        _ios_provisioning_profile_info =
-            exec_script("//build/config/ios/codesign.py",
-                        _find_provisioning_profile_args,
-                        "json")
-
-        xcode_extra_attributes = {
-          IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
-          CODE_SIGN_IDENTITY = "iPhone Developer"
-          DEVELOPMENT_TEAM = _ios_provisioning_profile_info.team_identifier
-          PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
-          PROVISIONING_PROFILE_SPECIFIER = _ios_provisioning_profile_info.name
-
-          # For XCUITest, Xcode requires specifying the host application name
-          # via the TEST_TARGET_NAME attribute.
-          if (invoker.product_type == _ios_xcode_xcuitest_bundle_id) {
-            TEST_TARGET_NAME = invoker.xcode_test_application_name
+        # For XCTest, Xcode requires specifying the host application path via
+        # both BUNDLE_LOADER and TEST_HOST attributes.
+        if (invoker.product_type == _ios_xcode_xctest_bundle_id) {
+          _xcode_app_name = invoker.xcode_test_application_name
+          if (defined(invoker.xcode_test_application_output_name)) {
+            _xcode_app_name = invoker.xcode_test_application_output_name
           }
 
-          # For XCTest, Xcode requires specifying the host application path via
-          # both BUNDLE_LOADER and TEST_HOST attributes.
-          if (invoker.product_type == _ios_xcode_xctest_bundle_id) {
-            _xcode_app_name = invoker.xcode_test_application_name
-            if (defined(invoker.xcode_test_application_output_name)) {
-              _xcode_app_name = invoker.xcode_test_application_output_name
-            }
-
-            BUNDLE_LOADER = "\$(TEST_HOST)"
-            TEST_HOST = "\$(BUILT_PRODUCTS_DIR)/" +
-                        "${_xcode_app_name}.app/${_xcode_app_name}"
-          }
+          BUNDLE_LOADER = "\$(TEST_HOST)"
+          TEST_HOST = "\$(BUILT_PRODUCTS_DIR)/" +
+                      "${_xcode_app_name}.app/${_xcode_app_name}"
         }
-      } else {
-        not_needed(invoker,
-                   [
-                     "xcode_test_application_name",
-                     "xcode_test_application_output_name",
-                   ])
       }
 
       deps = [ ":$_info_plist_bundle" ]
diff --git a/build/fuchsia/cipd/BUILD.gn b/build/fuchsia/cipd/BUILD.gn
index 3547d74..52f69a77 100644
--- a/build/fuchsia/cipd/BUILD.gn
+++ b/build/fuchsia/cipd/BUILD.gn
@@ -294,7 +294,6 @@
       far_sources = [
         "${root_gen_dir}/base/base_unittests/base_unittests.far",
         "${root_gen_dir}/ipc/ipc_tests/ipc_tests.far",
-        "${root_gen_dir}/media/media_unittests/media_unittests.far",
         "${root_gen_dir}/mojo/mojo_unittests/mojo_unittests.far",
         "${root_gen_dir}/third_party/blink/common/blink_common_unittests/blink_common_unittests.far",
       ]
@@ -302,6 +301,7 @@
         "${root_gen_dir}/fuchsia_web/runners/cast_runner_integration_tests_cfv1/cast_runner_integration_tests_cfv1.far",
         "${root_gen_dir}/fuchsia_web/runners/web_runner_integration_tests/web_runner_integration_tests.far",
         "${root_gen_dir}/fuchsia_web/webengine/web_engine_integration_tests/web_engine_integration_tests.far",
+        "${root_gen_dir}/media/media_unittests/media_unittests.far",
         "${root_gen_dir}/skia/skia_unittests/skia_unittests.far",
       ]
     },
@@ -310,12 +310,13 @@
       far_sources = [
         "${root_gen_dir}/base/base_unittests/base_unittests.far",
         "${root_gen_dir}/ipc/ipc_tests/ipc_tests.far",
-        "${root_gen_dir}/media/media_unittests/media_unittests.far",
         "${root_gen_dir}/mojo/mojo_unittests/mojo_unittests.far",
         "${root_gen_dir}/third_party/blink/common/blink_common_unittests/blink_common_unittests.far",
       ]
-      cfv1_far_sources =
-          [ "${root_gen_dir}/skia/skia_unittests/skia_unittests.far" ]
+      cfv1_far_sources = [
+        "${root_gen_dir}/media/media_unittests/media_unittests.far",
+        "${root_gen_dir}/skia/skia_unittests/skia_unittests.far",
+      ]
     },
     {
       manifest_path = "${target_gen_dir}/web_engine_tests_manifest.json"
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 5f601abd..a161029d 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220626.3.1
+8.20220627.1.1
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 70bd832..9561ff8a 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -275,6 +275,14 @@
 static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)};
 #undef M
 
+using MoveFunction = void (*)(PaintOp* op, char* dst);
+#define M(T)                                        \
+  [](PaintOp* op, char* dst) {                      \
+    new (dst) T(std::move(*(static_cast<T*>(op)))); \
+  },
+static const MoveFunction g_move_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
 #define M(T) T::kIsDrawOp,
 static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)};
 #undef M
@@ -1553,8 +1561,8 @@
 }
 
 void ConcatOp::Raster(const ConcatOp* op,
-                        SkCanvas* canvas,
-                        const PlaybackParams& params) {
+                      SkCanvas* canvas,
+                      const PlaybackParams& params) {
   canvas->concat(op->matrix);
 }
 
@@ -1931,8 +1939,8 @@
 }
 
 void SetMatrixOp::Raster(const SetMatrixOp* op,
-                           SkCanvas* canvas,
-                           const PlaybackParams& params) {
+                         SkCanvas* canvas,
+                         const PlaybackParams& params) {
   canvas->setMatrix(params.original_ctm * op->matrix);
 }
 
@@ -2687,6 +2695,12 @@
     func(this);
 }
 
+void PaintOp::MoveThis(char* dst) {
+  auto func = g_move_functions[type];
+  if (func)
+    func(this, dst);
+}
+
 bool PaintOpWithFlags::HasDiscardableImagesFromFlags() const {
   return flags.HasDiscardableImages();
 }
@@ -2770,6 +2784,8 @@
       rect(rect),
       data(std::move(data)) {}
 
+AnnotateOp::AnnotateOp(AnnotateOp&&) = default;
+
 AnnotateOp::~AnnotateOp() = default;
 
 DrawImageOp::DrawImageOp() : PaintOpWithFlags(kType) {}
@@ -2792,6 +2808,8 @@
       top(top),
       sampling(sampling) {}
 
+DrawImageOp::DrawImageOp(DrawImageOp&&) = default;
+
 bool DrawImageOp::HasDiscardableImages() const {
   return image && !image.IsTextureBacked();
 }
@@ -2824,6 +2842,8 @@
       sampling(sampling),
       constraint(constraint) {}
 
+DrawImageRectOp::DrawImageRectOp(DrawImageRectOp&&) = default;
+
 bool DrawImageRectOp::HasDiscardableImages() const {
   return image && !image.IsTextureBacked();
 }
@@ -2833,6 +2853,8 @@
 DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record)
     : PaintOp(kType), record(std::move(record)) {}
 
+DrawRecordOp::DrawRecordOp(DrawRecordOp&&) = default;
+
 DrawRecordOp::~DrawRecordOp() = default;
 
 size_t DrawRecordOp::AdditionalBytesUsed() const {
@@ -2859,6 +2881,8 @@
 
 DrawSkottieOp::DrawSkottieOp() : PaintOp(kType) {}
 
+DrawSkottieOp::DrawSkottieOp(DrawSkottieOp&&) = default;
+
 DrawSkottieOp::~DrawSkottieOp() = default;
 
 bool DrawSkottieOp::HasDiscardableImages() const {
@@ -2888,6 +2912,8 @@
       y(y),
       node_id(node_id) {}
 
+DrawTextBlobOp::DrawTextBlobOp(DrawTextBlobOp&&) = default;
+
 DrawTextBlobOp::~DrawTextBlobOp() = default;
 
 PaintOpBuffer::CompositeIterator::CompositeIterator(
@@ -3247,8 +3273,14 @@
   DCHECK_GE(new_size, used_);
   std::unique_ptr<char, base::AlignedFreeDeleter> new_data(
       static_cast<char*>(base::AlignedAlloc(new_size, PaintOpAlign)));
-  if (data_)
-    memcpy(new_data.get(), data_.get(), used_);
+  char* dst = new_data.get();
+  DCHECK(dst);
+  for (auto* op : Iterator(this)) {
+    size_t skip = op->skip;
+    op->MoveThis(dst);
+    op->DestroyThis();
+    dst += skip;
+  }
   data_ = std::move(new_data);
   reserved_ = new_size;
 }
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 06eedb5f..75bd3f8f 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -62,6 +62,7 @@
   explicit ThreadsafePath(const SkPath& path) : SkPath(path) {
     updateBoundsCache();
   }
+  ThreadsafePath(ThreadsafePath&&) = default;
   ThreadsafePath() { updateBoundsCache(); }
 };
 
@@ -312,6 +313,8 @@
   // memory buffers and so don't have their destructors run automatically.
   void DestroyThis();
 
+  void MoveThis(char* dst);
+
   // DrawColor is more restrictive on the blend modes that can be used.
   static bool IsValidDrawColorSkBlendMode(SkBlendMode mode) {
     return static_cast<uint32_t>(mode) <=
@@ -394,6 +397,7 @@
   AnnotateOp(PaintCanvas::AnnotationType annotation_type,
              const SkRect& rect,
              sk_sp<SkData> data);
+  AnnotateOp(AnnotateOp&&);
   ~AnnotateOp();
   static void Raster(const AnnotateOp* op,
                      SkCanvas* canvas,
@@ -422,6 +426,7 @@
         op(op),
         antialias(antialias),
         use_cache(use_paint_cache) {}
+  ClipPathOp(ClipPathOp&&) = default;
   static void Raster(const ClipPathOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -445,6 +450,7 @@
   static constexpr PaintOpType kType = PaintOpType::ClipRect;
   ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias)
       : PaintOp(kType), rect(rect), op(op), antialias(antialias) {}
+  ClipRectOp(ClipRectOp&&) = default;
   static void Raster(const ClipRectOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -465,6 +471,7 @@
   static constexpr PaintOpType kType = PaintOpType::ClipRRect;
   ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias)
       : PaintOp(kType), rrect(rrect), op(op), antialias(antialias) {}
+  ClipRRectOp(ClipRRectOp&&) = default;
   static void Raster(const ClipRRectOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -485,6 +492,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Concat;
   explicit ConcatOp(const SkM44& matrix) : PaintOp(kType), matrix(matrix) {}
+  ConcatOp(ConcatOp&&) = default;
   static void Raster(const ConcatOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -502,6 +510,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::CustomData;
   explicit CustomDataOp(uint32_t id) : PaintOp(kType), id(id) {}
+  CustomDataOp(CustomDataOp&&) = default;
   static void Raster(const CustomDataOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -522,6 +531,7 @@
   static constexpr bool kIsDrawOp = true;
   DrawColorOp(SkColor4f color, SkBlendMode mode)
       : PaintOp(kType), color(color), mode(mode) {}
+  DrawColorOp(DrawColorOp&&) = default;
   static void Raster(const DrawColorOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -544,6 +554,7 @@
                const SkRRect& inner,
                const PaintFlags& flags)
       : PaintOpWithFlags(kType, flags), outer(outer), inner(inner) {}
+  DrawDRRectOp(DrawDRRectOp&&) = default;
   static void RasterWithFlags(const DrawDRRectOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -571,6 +582,7 @@
               SkScalar top,
               const SkSamplingOptions&,
               const PaintFlags* flags);
+  DrawImageOp(DrawImageOp&&);
   ~DrawImageOp();
   static void RasterWithFlags(const DrawImageOp* op,
                               const PaintFlags* flags,
@@ -612,6 +624,7 @@
                   const SkSamplingOptions&,
                   const PaintFlags* flags,
                   SkCanvas::SrcRectConstraint constraint);
+  DrawImageRectOp(DrawImageRectOp&&);
   ~DrawImageRectOp();
   static void RasterWithFlags(const DrawImageRectOp* op,
                               const PaintFlags* flags,
@@ -671,6 +684,7 @@
              SkScalar y1,
              const PaintFlags& flags)
       : PaintOpWithFlags(kType, flags), x0(x0), y0(y0), x1(x1), y1(y1) {}
+  DrawLineOp(DrawLineOp&&) = default;
   static void RasterWithFlags(const DrawLineOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -696,6 +710,7 @@
   static constexpr bool kIsDrawOp = true;
   DrawOvalOp(const SkRect& oval, const PaintFlags& flags)
       : PaintOpWithFlags(kType, flags), oval(oval) {}
+  DrawOvalOp(DrawOvalOp&&) = default;
   static void RasterWithFlags(const DrawOvalOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -724,6 +739,7 @@
         path(path),
         sk_path_fill_type(static_cast<uint8_t>(path.getFillType())),
         use_cache(use_paint_cache) {}
+  DrawPathOp(DrawPathOp&&) = default;
   static void RasterWithFlags(const DrawPathOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -751,6 +767,7 @@
   static constexpr PaintOpType kType = PaintOpType::DrawRecord;
   static constexpr bool kIsDrawOp = true;
   explicit DrawRecordOp(sk_sp<const PaintRecord> record);
+  DrawRecordOp(DrawRecordOp&&);
   ~DrawRecordOp();
   static void Raster(const DrawRecordOp* op,
                      SkCanvas* canvas,
@@ -777,6 +794,7 @@
   static constexpr bool kIsDrawOp = true;
   DrawRectOp(const SkRect& rect, const PaintFlags& flags)
       : PaintOpWithFlags(kType, flags), rect(rect) {}
+  DrawRectOp(DrawRectOp&&) = default;
   static void RasterWithFlags(const DrawRectOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -797,6 +815,7 @@
   static constexpr bool kIsDrawOp = true;
   DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags)
       : PaintOpWithFlags(kType, flags), rrect(rrect) {}
+  DrawRRectOp(DrawRRectOp&&) = default;
   static void RasterWithFlags(const DrawRRectOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -821,6 +840,7 @@
                 SkottieFrameDataMap images,
                 const SkottieColorMap& color_map,
                 SkottieTextPropertyValueMap text_map);
+  DrawSkottieOp(DrawSkottieOp&&);
   ~DrawSkottieOp();
   static void Raster(const DrawSkottieOp* op,
                      SkCanvas* canvas,
@@ -872,6 +892,7 @@
                  SkScalar y,
                  NodeId node_id,
                  const PaintFlags& flags);
+  DrawTextBlobOp(DrawTextBlobOp&&);
   ~DrawTextBlobOp();
   static void RasterWithFlags(const DrawTextBlobOp* op,
                               const PaintFlags* flags,
@@ -898,6 +919,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Noop;
   NoopOp() : PaintOp(kType) {}
+  NoopOp(NoopOp&&) = default;
   static void Raster(const NoopOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params) {}
@@ -910,6 +932,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Restore;
   RestoreOp() : PaintOp(kType) {}
+  RestoreOp(RestoreOp&&) = default;
   static void Raster(const RestoreOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -922,6 +945,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Rotate;
   explicit RotateOp(SkScalar degrees) : PaintOp(kType), degrees(degrees) {}
+  RotateOp(RotateOp&&) = default;
   static void Raster(const RotateOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -939,6 +963,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Save;
   SaveOp() : PaintOp(kType) {}
+  SaveOp(SaveOp&&) = default;
   static void Raster(const SaveOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -953,6 +978,7 @@
   SaveLayerOp(const SkRect* bounds, const PaintFlags* flags)
       : PaintOpWithFlags(kType, flags ? *flags : PaintFlags()),
         bounds(bounds ? *bounds : kUnsetRect) {}
+  SaveLayerOp(SaveLayerOp&&) = default;
   static void RasterWithFlags(const SaveLayerOp* op,
                               const PaintFlags* flags,
                               SkCanvas* canvas,
@@ -978,6 +1004,7 @@
   static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha;
   SaveLayerAlphaOp(const SkRect* bounds, uint8_t alpha)
       : PaintOp(kType), bounds(bounds ? *bounds : kUnsetRect), alpha(alpha) {}
+  SaveLayerAlphaOp(SaveLayerAlphaOp&&) = default;
   static void Raster(const SaveLayerAlphaOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -998,6 +1025,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Scale;
   ScaleOp(SkScalar sx, SkScalar sy) : PaintOp(kType), sx(sx), sy(sy) {}
+  ScaleOp(ScaleOp&&) = default;
   static void Raster(const ScaleOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -1016,6 +1044,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::SetMatrix;
   explicit SetMatrixOp(const SkM44& matrix) : PaintOp(kType), matrix(matrix) {}
+  SetMatrixOp(SetMatrixOp&&) = default;
   // This is the only op that needs the original ctm of the SkCanvas
   // used for raster (since SetMatrix is relative to the recording origin and
   // shouldn't clobber the SkCanvas raster origin).
@@ -1039,6 +1068,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::SetNodeId;
   explicit SetNodeIdOp(int node_id) : PaintOp(kType), node_id(node_id) {}
+  SetNodeIdOp(SetNodeIdOp&&) = default;
   static void Raster(const SetNodeIdOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
@@ -1056,6 +1086,7 @@
  public:
   static constexpr PaintOpType kType = PaintOpType::Translate;
   TranslateOp(SkScalar dx, SkScalar dy) : PaintOp(kType), dx(dx), dy(dy) {}
+  TranslateOp(TranslateOp&&) = default;
   static void Raster(const TranslateOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params);
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h
index 80519ed..582536b 100644
--- a/cc/paint/paint_shader.h
+++ b/cc/paint/paint_shader.h
@@ -263,7 +263,7 @@
   std::vector<SkColor> colors_;
   std::vector<SkScalar> positions_;
 
-  // Cached intermediates, for cc::Paint objects that may not be thread-safe
+  // Cached intermediates, for Paint objects that may not be thread-safe
   sk_sp<SkPicture> sk_cached_picture_;
   sk_sp<SkImage> sk_cached_image_;
 
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc
index ffaddba..4397d41 100644
--- a/cc/trees/effect_node.cc
+++ b/cc/trees/effect_node.cc
@@ -189,7 +189,8 @@
     }
     if (mask_filter_info.HasGradientMask()) {
       MathUtil::AddToTracedValue("mask_filter_gradient_mask",
-                                 mask_filter_info.gradient_mask(), value);
+                                 mask_filter_info.gradient_mask().value(),
+                                 value);
     }
   }
   value->SetString("blend_mode", SkBlendMode_Name(blend_mode));
diff --git a/cc/trees/property_tree_builder_unittest.cc b/cc/trees/property_tree_builder_unittest.cc
index 5b5f96f..fe8ecc92 100644
--- a/cc/trees/property_tree_builder_unittest.cc
+++ b/cc/trees/property_tree_builder_unittest.cc
@@ -809,10 +809,10 @@
     // |angle| is updated by the scale transform.
     EXPECT_EQ(33, layer_impl1->draw_properties()
                       .mask_filter_info.gradient_mask()
-                      .angle());
+                      ->angle());
     EXPECT_EQ(gradient_mask.steps(), layer_impl1->draw_properties()
                                          .mask_filter_info.gradient_mask()
-                                         .steps());
+                                         ->steps());
   }
 
   // Rotate transform eliminates gradient mask.
@@ -878,10 +878,11 @@
     EXPECT_EQ(gfx::RectF(10, 10, 300, 200),
               render_surface_impl1->mask_filter_info().bounds());
     // |angle| is updated by the scale transform.
-    EXPECT_EQ(33,
-              render_surface_impl1->mask_filter_info().gradient_mask().angle());
-    EXPECT_EQ(gradient_mask.steps(),
-              render_surface_impl1->mask_filter_info().gradient_mask().steps());
+    EXPECT_EQ(
+        33, render_surface_impl1->mask_filter_info().gradient_mask()->angle());
+    EXPECT_EQ(
+        gradient_mask.steps(),
+        render_surface_impl1->mask_filter_info().gradient_mask()->steps());
   }
 
   // Rotate transform eliminates gradient mask.
@@ -960,11 +961,12 @@
     // |mask_info| coordinates are in the target space of the render surface's
     // layer.
     auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
-    EXPECT_EQ(gradient_mask1.steps(),
-              render_surface_impl1->mask_filter_info().gradient_mask().steps());
+    EXPECT_EQ(
+        gradient_mask1.steps(),
+        render_surface_impl1->mask_filter_info().gradient_mask()->steps());
     // |angle| is updated by the scale transform.
-    EXPECT_EQ(21,
-              render_surface_impl1->mask_filter_info().gradient_mask().angle());
+    EXPECT_EQ(
+        21, render_surface_impl1->mask_filter_info().gradient_mask()->angle());
 
     // |mask_info| is in the coordinate space of the transform node associated
     // with this effect node.
@@ -979,9 +981,9 @@
     EXPECT_EQ(gfx::RectF(30, 10, 300, 150),
               draw_properties2.mask_filter_info.bounds());
     // |angle| is updated by the scale transform.
-    EXPECT_EQ(26, draw_properties2.mask_filter_info.gradient_mask().angle());
+    EXPECT_EQ(26, draw_properties2.mask_filter_info.gradient_mask()->angle());
     EXPECT_EQ(gradient_mask2.steps(),
-              draw_properties2.mask_filter_info.gradient_mask().steps());
+              draw_properties2.mask_filter_info.gradient_mask()->steps());
   }
 
   gfx::Transform rotate_transform;
diff --git a/chrome/VERSION b/chrome/VERSION
index 5eb2ebf..1a26532 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=105
 MINOR=0
-BUILD=5146
+BUILD=5147
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 089a050..3ecb4ac 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -438,6 +438,7 @@
     "//chrome/browser/ui/android/appmenu:factory_java",
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/default_browser_promo:java",
+    "//chrome/browser/ui/android/fast_checkout:java",
     "//chrome/browser/ui/android/favicon:java",
     "//chrome/browser/ui/android/layouts:java",
     "//chrome/browser/ui/android/layouts/glue:java",
@@ -801,6 +802,7 @@
     "//chrome/browser/touch_to_fill/android/internal:java",
     "//chrome/browser/ui/android/appmenu/internal:java",
     "//chrome/browser/ui/android/autofill/internal:java",
+    "//chrome/browser/ui/android/fast_checkout/internal:java",
     "//chrome/browser/ui/android/webid/internal:java",
     "//chrome/browser/video_tutorials/internal:java",
     "//components/browser_ui/bottomsheet/android/internal:java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 122551c..045d72a 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -207,7 +207,7 @@
   "java/src/org/chromium/chrome/browser/browserservices/BrowserServicesStore.java",
   "java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java",
   "java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorder.java",
-  "java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java",
+  "java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRecorder.java",
   "java/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRegister.java",
   "java/src/org/chromium/chrome/browser/browserservices/InstalledWebappRegistrar.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 1c1a94e..932d985 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -35,7 +35,7 @@
   "junit/src/org/chromium/chrome/browser/base/SplitPreloaderTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java",
-  "junit/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiverTest.java",
+  "junit/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiverTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRecorderTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRegisterTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
index 5c4ed3d..679bca59 100644
--- a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
@@ -963,15 +963,15 @@
         android:name="org.chromium.chrome.browser.app.send_tab_to_self.SendTabToSelfNotificationReceiver"
         android:exported="false">
     </receiver>  # DIFF-ANCHOR: f6d77f5a
-    <receiver  # DIFF-ANCHOR: 31a8399a
-        android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver"
+    <receiver  # DIFF-ANCHOR: bb762b77
+        android:name="org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver"
         android:exported="true">
       <intent-filter>  # DIFF-ANCHOR: e5bb6a36
         <action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
         <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
         <data android:scheme="package"/>
       </intent-filter>  # DIFF-ANCHOR: e5bb6a36
-    </receiver>  # DIFF-ANCHOR: 31a8399a
+    </receiver>  # DIFF-ANCHOR: bb762b77
     <receiver  # DIFF-ANCHOR: 2f4b69ac
         android:name="org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.DisclosureAcceptanceBroadcastReceiver"
         android:exported="false">
diff --git a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
index 8aa262a0..8a50f1a 100644
--- a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
@@ -734,15 +734,15 @@
         android:name="org.chromium.chrome.browser.app.send_tab_to_self.SendTabToSelfNotificationReceiver"
         android:exported="false">
     </receiver>  # DIFF-ANCHOR: f6d77f5a
-    <receiver  # DIFF-ANCHOR: 31a8399a
-        android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver"
+    <receiver  # DIFF-ANCHOR: bb762b77
+        android:name="org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver"
         android:exported="true">
       <intent-filter>  # DIFF-ANCHOR: e5bb6a36
         <action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
         <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
         <data android:scheme="package"/>
       </intent-filter>  # DIFF-ANCHOR: e5bb6a36
-    </receiver>  # DIFF-ANCHOR: 31a8399a
+    </receiver>  # DIFF-ANCHOR: bb762b77
     <receiver  # DIFF-ANCHOR: 2f4b69ac
         android:name="org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.DisclosureAcceptanceBroadcastReceiver"
         android:exported="false">
diff --git a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
index dac7160..d42344b2 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
@@ -889,15 +889,15 @@
         android:name="org.chromium.chrome.browser.app.send_tab_to_self.SendTabToSelfNotificationReceiver"
         android:exported="false">
     </receiver>  # DIFF-ANCHOR: f6d77f5a
-    <receiver  # DIFF-ANCHOR: 31a8399a
-        android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver"
+    <receiver  # DIFF-ANCHOR: bb762b77
+        android:name="org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver"
         android:exported="true">
       <intent-filter>  # DIFF-ANCHOR: e5bb6a36
         <action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
         <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
         <data android:scheme="package"/>
       </intent-filter>  # DIFF-ANCHOR: e5bb6a36
-    </receiver>  # DIFF-ANCHOR: 31a8399a
+    </receiver>  # DIFF-ANCHOR: bb762b77
     <receiver  # DIFF-ANCHOR: 2f4b69ac
         android:name="org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.DisclosureAcceptanceBroadcastReceiver"
         android:exported="false">
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index f333b2c4..c3924d2 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -814,7 +814,7 @@
         </activity>
 
         <!-- Components for Trusted Web Activities -->
-        <receiver android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver"
+        <receiver android:name="org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver"
             android:exported="true">
             <intent-filter>
                 <data android:scheme="package" />
@@ -823,7 +823,7 @@
             </intent-filter>
             {% if channel in ['default'] %}
             <intent-filter>
-                <action android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver.DEBUG" />
+                <action android:name="org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver.DEBUG" />
             </intent-filter>
             {% endif %}
         </receiver>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
index 92de6bf..fb4590d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
@@ -88,8 +88,9 @@
 
     private void recordDecision(boolean accepted) {
         boolean appUninstalled = getIsAppUninstalledFromIntent(getIntent());
-        ChromeApplicationImpl.getComponent().resolveTwaClearDataDialogRecorder().handleDialogResult(
-                accepted, appUninstalled);
+        ChromeApplicationImpl.getComponent()
+                .resolveClearDataDialogResultRecorder()
+                .handleDialogResult(accepted, appUninstalled);
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java
similarity index 89%
rename from chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java
rename to chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java
index 66bd545..2f5a982 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java
@@ -25,7 +25,7 @@
 import javax.inject.Inject;
 
 /**
- * A {@link android.content.BroadcastReceiver} that detects when a Trusted Web Activity client app
+ * A {@link android.content.BroadcastReceiver} that detects when an installed webapp (TWA or WebAPK)
  * has been uninstalled or has had its data cleared. When this happens we clear Chrome's data
  * corresponding to that app.
  *
@@ -47,22 +47,22 @@
  * Lifecycle: The lifecycle of this class is managed by Android.
  * Thread safety: {@link #onReceive} will be called on the UI thread.
  */
-public class ClientAppBroadcastReceiver extends BroadcastReceiver {
-    private static final String TAG = "ClientAppBroadRec";
+public class InstalledWebappBroadcastReceiver extends BroadcastReceiver {
+    private static final String TAG = "IWBroadcastReceiver";
 
     /**
      * An Action that will trigger clearing data on local builds only, for development. The adb
      * command to trigger is:
      * adb shell am broadcast \
      *   -n com.google.android.apps.chrome/\
-     * org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver \
-     *   -a org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver.DEBUG \
+     * org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver \
+     *   -a org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver.DEBUG \
      *   --ei android.intent.extra.UID 23
      *
      * But replace 23 with the uid of a Trusted Web Activity Client app.
      */
     private static final String ACTION_DEBUG =
-            "org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver.DEBUG";
+            "org.chromium.chrome.browser.browserservices.InstalledWebappBroadcastReceiver.DEBUG";
 
     private static final Set<String> BROADCASTS = new HashSet<>(Arrays.asList(
             Intent.ACTION_PACKAGE_DATA_CLEARED,
@@ -76,15 +76,15 @@
 
     /** Constructor with default dependencies for Android. */
     @Inject
-    public ClientAppBroadcastReceiver() {
+    public InstalledWebappBroadcastReceiver() {
         this(new ClearDataStrategy(), new InstalledWebappDataRegister(),
                 new BrowserServicesStore(
                         ChromeApplicationImpl.getComponent().resolveSharedPreferencesManager()),
-                ChromeApplicationImpl.getComponent().resolveTwaPermissionUpdater());
+                ChromeApplicationImpl.getComponent().resolvePermissionUpdater());
     }
 
     /** Constructor to allow dependency injection in tests. */
-    public ClientAppBroadcastReceiver(ClearDataStrategy strategy,
+    public InstalledWebappBroadcastReceiver(ClearDataStrategy strategy,
             InstalledWebappDataRegister dataRegister, BrowserServicesStore store,
             PermissionUpdater permissionUpdater) {
         mClearDataStrategy = strategy;
@@ -154,8 +154,8 @@
             }
 
             String appName = dataRegister.getAppNameForRegisteredUid(uid);
-            Intent intent = ClearDataDialogActivity
-                    .createIntent(context, appName, domains, origins, uninstalled);
+            Intent intent = ClearDataDialogActivity.createIntent(
+                    context, appName, domains, origins, uninstalled);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
             context.startActivity(intent);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java
index ea247e26..5e64f42c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java
@@ -63,7 +63,7 @@
     private final TrustedWebActivityUmaRecorder mUmaRecorder;
 
     // Use a Lazy instance so we don't instantiate it on Android versions pre-O.
-    private final Lazy<NotificationChannelPreserver> mPermissionPreserver;
+    private final Lazy<NotificationChannelPreserver> mChannelPreserver;
 
     public static InstalledWebappPermissionManager get() {
         return ChromeApplicationImpl.getComponent().resolvePermissionManager();
@@ -71,11 +71,12 @@
 
     @Inject
     public InstalledWebappPermissionManager(@Named(APP_CONTEXT) Context context,
-            InstalledWebappPermissionStore store, Lazy<NotificationChannelPreserver> preserver,
+            InstalledWebappPermissionStore store,
+            Lazy<NotificationChannelPreserver> channelPreserver,
             TrustedWebActivityUmaRecorder umaRecorder) {
         mPackageManager = context.getPackageManager();
         mStore = store;
-        mPermissionPreserver = preserver;
+        mChannelPreserver = channelPreserver;
         mUmaRecorder = umaRecorder;
     }
 
@@ -144,7 +145,7 @@
                 mStore.setStateForOrigin(origin, packageName, appName, type, enabled);
 
         if (type == ContentSettingsType.NOTIFICATIONS) {
-            NotificationChannelPreserver.deleteChannelIfNeeded(mPermissionPreserver, origin);
+            NotificationChannelPreserver.deleteChannelIfNeeded(mChannelPreserver, origin);
         }
 
         if (stateChanged) {
@@ -179,7 +180,7 @@
                 mStore.setStateForOrigin(origin, packageName, appName, type, settingValue);
 
         if (type == ContentSettingsType.NOTIFICATIONS) {
-            NotificationChannelPreserver.deleteChannelIfNeeded(mPermissionPreserver, origin);
+            NotificationChannelPreserver.deleteChannelIfNeeded(mChannelPreserver, origin);
         }
 
         if (stateChanged) {
@@ -191,7 +192,7 @@
     void unregister(Origin origin) {
         mStore.removeOrigin(origin);
 
-        NotificationChannelPreserver.restoreChannelIfNeeded(mPermissionPreserver, origin);
+        NotificationChannelPreserver.restoreChannelIfNeeded(mChannelPreserver, origin);
 
         InstalledWebappBridge.notifyPermissionsChange(ContentSettingsType.NOTIFICATIONS);
         InstalledWebappBridge.notifyPermissionsChange(ContentSettingsType.GEOLOCATION);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/PermissionUpdater.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/PermissionUpdater.java
index 5c2145e..9aef41d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/PermissionUpdater.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/PermissionUpdater.java
@@ -41,7 +41,7 @@
     }
 
     public static PermissionUpdater get() {
-        return ChromeApplicationImpl.getComponent().resolveTwaPermissionUpdater();
+        return ChromeApplicationImpl.getComponent().resolvePermissionUpdater();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
index 223597f..da8c6e94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
@@ -35,9 +35,9 @@
 
     CustomTabsConnection resolveCustomTabsConnection();
     SharedPreferencesManager resolveSharedPreferencesManager();
-    ClearDataDialogResultRecorder resolveTwaClearDataDialogRecorder();
+    ClearDataDialogResultRecorder resolveClearDataDialogResultRecorder();
     InstalledWebappPermissionManager resolvePermissionManager();
-    PermissionUpdater resolveTwaPermissionUpdater();
+    PermissionUpdater resolvePermissionUpdater();
     TrustedWebActivityClient resolveTrustedWebActivityClient();
     ExternalAuthUtils resolveExternalAuthUtils();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 77519ef00..b3c1f4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -432,7 +432,7 @@
 
         deferredStartupHandler.addDeferredTask(
                 ChromeApplicationImpl.getComponent()
-                        .resolveTwaClearDataDialogRecorder()::makeDeferredRecordings);
+                        .resolveClearDataDialogResultRecorder()::makeDeferredRecordings);
         deferredStartupHandler.addDeferredTask(WebApkUninstallUmaTracker::recordDeferredUma);
 
         deferredStartupHandler.addDeferredTask(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiverTest.java
similarity index 83%
rename from chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiverTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiverTest.java
index b9ca6133..21316dd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiverTest.java
@@ -37,24 +37,23 @@
 import java.util.Set;
 
 /**
- * Tests for {@link ClientAppBroadcastReceiver}.
+ * Tests for {@link InstalledWebappBroadcastReceiver}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-public class ClientAppBroadcastReceiverTest {
+public class InstalledWebappBroadcastReceiverTest {
     @Mock public Context mContext;
     @Mock public InstalledWebappDataRegister mDataRegister;
-    @Mock public ClientAppBroadcastReceiver.ClearDataStrategy mMockStrategy;
-    @Mock
-    public PermissionUpdater mPermissionUpdater;
+    @Mock public InstalledWebappBroadcastReceiver.ClearDataStrategy mMockStrategy;
+    @Mock public PermissionUpdater mPermissionUpdater;
 
-    private ClientAppBroadcastReceiver mReceiver;
+    private InstalledWebappBroadcastReceiver mReceiver;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mReceiver = new ClientAppBroadcastReceiver(
+        mReceiver = new InstalledWebappBroadcastReceiver(
                 mMockStrategy, mDataRegister, mock(BrowserServicesStore.class), mPermissionUpdater);
         mContext = RuntimeEnvironment.application;
     }
@@ -66,18 +65,14 @@
         return intent;
     }
 
-    private void addToRegister(int id, String appName, Set<String> domainAndRegistries,
-            Set<String> origins) {
+    private void addToRegister(
+            int id, String appName, Set<String> domainAndRegistries, Set<String> origins) {
         doReturn(true).when(mDataRegister).chromeHoldsDataForPackage(eq(id));
         doReturn(appName).when(mDataRegister).getAppNameForRegisteredUid(eq(id));
-        doReturn(domainAndRegistries)
-                .when(mDataRegister)
-                .getDomainsForRegisteredUid(eq(id));
+        doReturn(domainAndRegistries).when(mDataRegister).getDomainsForRegisteredUid(eq(id));
 
         if (origins == null) return;
-        doReturn(origins)
-                .when(mDataRegister)
-                .getOriginsForRegisteredUid(eq(id));
+        doReturn(origins).when(mDataRegister).getOriginsForRegisteredUid(eq(id));
     }
 
     private void addToRegister(int id, String appName, Set<String> domainAndRegistries) {
@@ -113,9 +108,9 @@
     @Test
     @Feature("TrustedWebActivities")
     public void execute_ValidIntent() {
-        mReceiver =
-                new ClientAppBroadcastReceiver(new ClientAppBroadcastReceiver.ClearDataStrategy(),
-                        mDataRegister, mock(BrowserServicesStore.class), mPermissionUpdater);
+        mReceiver = new InstalledWebappBroadcastReceiver(
+                new InstalledWebappBroadcastReceiver.ClearDataStrategy(), mDataRegister,
+                mock(BrowserServicesStore.class), mPermissionUpdater);
 
         int id = 67;
         String appName = "App Name 3";
@@ -141,9 +136,9 @@
     @Test
     @Feature("TrustedwebActivities")
     public void execute_UpdatePermissions() {
-        mReceiver =
-                new ClientAppBroadcastReceiver(new ClientAppBroadcastReceiver.ClearDataStrategy(),
-                        mDataRegister, mock(BrowserServicesStore.class), mPermissionUpdater);
+        mReceiver = new InstalledWebappBroadcastReceiver(
+                new InstalledWebappBroadcastReceiver.ClearDataStrategy(), mDataRegister,
+                mock(BrowserServicesStore.class), mPermissionUpdater);
 
         int id = 67;
         String appName = "App Name 3";
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 4b0554a943..af79d96 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1361,14 +1361,11 @@
   <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_SUBTITLE" desc="The sub-title of the dialog that allows user to opt-in into several Google marketing options.">
     Get <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> tips, offers, and updates, and share feedback.
   </message>
-  <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_1" desc="The first line of of an alternate title for  the last dialog the marketing opt-in for cloud gaming devices.">
-    Setup is complete!
+  <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE" desc="An alternate title for the last dialog the marketing opt-in for cloud gaming devices.">
+    Setup is complete! Get your device ready for gaming next
   </message>
-  <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_2" desc="The second line of of an alternate title for  the last dialog the marketing opt-in for cloud gaming devices.">
-    Get ready to start gaming
-  </message>
-  <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE" desc="An alternate subtitle for  the last dialog the marketing opt-in for cloud gaming devices.">
-    Open the Explore app after setup to start gaming. Access hundreds of the latest games, see gaming offers, and get an immersive gaming experience.
+  <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE" desc="An alternate subtitle for the last dialog the marketing opt-in for cloud gaming devices.">
+    Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> is built for gaming. The Explore app will open next where you can access hundreds of the latest games, see gaming offers, and discover gaming features that come with your device.
   </message>
   <message name="IDS_LOGIN_MARKETING_OPT_IN_SCREEN_UNSUBSCRIBE_SHORT" desc="Disclaimer about being able to unsubscribe">
     Unsubscribe anytime.
@@ -1507,6 +1504,27 @@
   <message name="IDS_LOGIN_PASSWORD_CHANGED_TRY_AGAIN" desc="Label for the retry button on the proceed anyway step in the the GAIA password changed flow">
     Try again
   </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE" desc="Title for the data loss warning screen if the user decided not to recover local data">
+    Local data will be deleted
+  </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P1" desc="First paragraph of subtitle message for the data loss warning screen if the user decided not to recover local data">
+    Local data is protected by your old password. If you recently changed your password, try your old password again.
+  </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P2" desc="Second paragraph of subtitle message for the data loss warning screen if the user decided not to recover local data">
+    If you continue without entering your old password, your local data will be deleted. You’ll sign in with your Google Account to set up this user again.
+  </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_TITLE" desc="Title for the screen to recover local data by entering old password.">
+    Restore your local data
+  </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_SUBTITLE" desc="Subtitle for the screen to recover local data by entering old password.">
+    Your password has changed. To restore your local data, you need to enter your old password.
+  </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_AND_DELETE_BUTTON" desc="Button to continue and delete data without providing old password on the data loss warning dialog.">
+    Continue and delete data
+  </message>
+  <message name="IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_WITHOUT_LOCAL_DATA_BUTTON" desc="Button to confirm continue without local data on the recover local data dialog.">
+    Continue without local data
+  </message>
   <message name="IDS_LOGIN_SAML_NOTICE" desc="Text message displayed above SAML portal to early indicate that the user is being redirected to another sign-in provider. This is the version of the string used in the GAIA flow.">
     This sign-in service is hosted by <ph name="SAML_DOMAIN">$1<ex>saml.com</ex></ph>
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SIGN_ME_UP.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SIGN_ME_UP.png.sha1
index 33911354..85348c2 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SIGN_ME_UP.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SIGN_ME_UP.png.sha1
@@ -1 +1 @@
-2ff47f4e1166beb4ca615162ca9fd8659c15218a
\ No newline at end of file
+7a621fe89c60fe3c593e890a4cbadb7159ef8e92
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE.png.sha1
index 33911354..85348c2 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE.png.sha1
@@ -1 +1 @@
-2ff47f4e1166beb4ca615162ca9fd8659c15218a
\ No newline at end of file
+7a621fe89c60fe3c593e890a4cbadb7159ef8e92
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE.png.sha1
new file mode 100644
index 0000000..a21fd1c
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE.png.sha1
@@ -0,0 +1 @@
+064240324efe803f8b8a2b5917f7cd1922a9793c
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_1.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_1.png.sha1
deleted file mode 100644
index 33911354..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_1.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2ff47f4e1166beb4ca615162ca9fd8659c15218a
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_2.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_2.png.sha1
deleted file mode 100644
index 33911354..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_2.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2ff47f4e1166beb4ca615162ca9fd8659c15218a
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_AND_DELETE_BUTTON.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_AND_DELETE_BUTTON.png.sha1
new file mode 100644
index 0000000..f749aba
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_AND_DELETE_BUTTON.png.sha1
@@ -0,0 +1 @@
+8d0789398d5167fe755ac89cd8255861356990b7
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_WITHOUT_LOCAL_DATA_BUTTON.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_WITHOUT_LOCAL_DATA_BUTTON.png.sha1
new file mode 100644
index 0000000..902cb35
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_WITHOUT_LOCAL_DATA_BUTTON.png.sha1
@@ -0,0 +1 @@
+6fedbf5d2f73182bbaf1abe33a1ee8af696cecdc
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P1.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P1.png.sha1
new file mode 100644
index 0000000..f749aba
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P1.png.sha1
@@ -0,0 +1 @@
+8d0789398d5167fe755ac89cd8255861356990b7
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P2.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P2.png.sha1
new file mode 100644
index 0000000..f749aba
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P2.png.sha1
@@ -0,0 +1 @@
+8d0789398d5167fe755ac89cd8255861356990b7
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE.png.sha1
new file mode 100644
index 0000000..f749aba
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE.png.sha1
@@ -0,0 +1 @@
+8d0789398d5167fe755ac89cd8255861356990b7
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_SUBTITLE.png.sha1
new file mode 100644
index 0000000..902cb35
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+6fedbf5d2f73182bbaf1abe33a1ee8af696cecdc
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_TITLE.png.sha1
new file mode 100644
index 0000000..902cb35
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_TITLE.png.sha1
@@ -0,0 +1 @@
+6fedbf5d2f73182bbaf1abe33a1ee8af696cecdc
\ No newline at end of file
diff --git a/chrome/app/resources/generated_resources_da.xtb b/chrome/app/resources/generated_resources_da.xtb
index 3b14637d..d59b8ce 100644
--- a/chrome/app/resources/generated_resources_da.xtb
+++ b/chrome/app/resources/generated_resources_da.xtb
@@ -159,6 +159,7 @@
 <translation id="1148063863818152153">Din enheds EID-nummer</translation>
 <translation id="1148624853678088576">Så er du klar</translation>
 <translation id="1149401351239820326">Udløbsmåned</translation>
+<translation id="1149483087970735785">Hjælpeteknologi</translation>
 <translation id="1149725087019908252">Scanner <ph name="FILE_NAME" /></translation>
 <translation id="1150490752229770117">Dette er den sidste automatiske software- og sikkerhedsopdatering for denne <ph name="DEVICE_TYPE" />. Opgrader til en nyere model for at få fremtidige opdateringer. <ph name="LINK_BEGIN" />Få flere oplysninger<ph name="LINK_END" /></translation>
 <translation id="1150565364351027703">Solbriller</translation>
@@ -733,6 +734,7 @@
 <translation id="1680849702532889074">Der opstod en fejl ved installationen af Linux-appen.</translation>
 <translation id="16815041330799488">Tillad ikke, at websites kan se tekst og billeder, der er kopieret til udklipsholderen</translation>
 <translation id="1682548588986054654">Nyt inkognitovindue</translation>
+<translation id="1682696837763999627">Stor musemarkør</translation>
 <translation id="1682867089915960590">Vil du aktivere tastenavigation?</translation>
 <translation id="1686550358074589746">Aktivér glidende indtastning</translation>
 <translation id="168715261339224929">Aktivér synkronisering for at se dine bogmærker på alle dine enheder.</translation>
@@ -844,6 +846,7 @@
 <translation id="178092663238929451">Konfigurer Deling tæt på for at modtage filer fra personer i nærheden samt sende filer til dem</translation>
 <translation id="1781291988450150470">Aktuel pinkode</translation>
 <translation id="1781502536226964113">Åbn siden Ny fane</translation>
+<translation id="1781553166608855614">Talt sprog</translation>
 <translation id="1781771911845953849">Konti og synkronisering</translation>
 <translation id="1781979858217752599">Del lyd fra vindue</translation>
 <translation id="1782101999402987960">Opdateringer blokeres af din administrator</translation>
@@ -1572,6 +1575,7 @@
 <translation id="244475495405467108">Luk fanerne til venstre</translation>
 <translation id="2445081178310039857">Udvidelsens rodmappe er påkrævet.</translation>
 <translation id="2445484935443597917">Opret en ny profil</translation>
+<translation id="2445726032315793326">Delvis forstørrelse</translation>
 <translation id="244641233057214044">Relateret til din søgning</translation>
 <translation id="2448312741937722512">Type</translation>
 <translation id="2448810255793562605">Automatisk scanning ved kontaktadgang</translation>
@@ -1864,6 +1868,7 @@
 <translation id="2724841811573117416">WebRTC-logfiler</translation>
 <translation id="272488616838512378">Enhedsomregning</translation>
 <translation id="2725200716980197196">Forbindelsen til netværket er genoprettet</translation>
+<translation id="272741954544380994">Søg på billedet med <ph name="VISUAL_SEARCH_PROVIDER" /></translation>
 <translation id="2727633948226935816">Påmind mig ikke igen</translation>
 <translation id="2727712005121231835">Faktisk størrelse</translation>
 <translation id="2729314457178420145">Ryd også browserdata (<ph name="URL" />), hvilket muligvis logger dig ud af Google.com. <ph name="LEARN_MORE" /></translation>
@@ -2205,6 +2210,7 @@
 <translation id="3030967311408872958">Fra solnedgang til solopgang</translation>
 <translation id="3031417829280473749">Scully</translation>
 <translation id="3031557471081358569">Vælg at importere følgende:</translation>
+<translation id="3032204772252313646">Automatiske undertekster</translation>
 <translation id="3033348223765101500">Administrer dine data</translation>
 <translation id="3034627908241330765">En anden Steam-konfiguration er i gang. Vent, indtil den er fuldført, før du starter konfigurationen igen.</translation>
 <translation id="3036327949511794916">Fristen for at returnere denne <ph name="DEVICE_TYPE" /> er overskredet.</translation>
@@ -2637,6 +2643,7 @@
 <translation id="3480612136143976912">Tilpas tekststørrelsen og -stilarten for Livetekstning. Nogle apps og websites vil også anvende denne indstilling.</translation>
 <translation id="3480827850068960424">Der blev fundet <ph name="NUM" /> faner</translation>
 <translation id="3481268647794498892">Åbner i <ph name="ALTERNATIVE_BROWSER_NAME" /> om <ph name="COUNTDOWN_SECONDS" /> sekunder</translation>
+<translation id="348268549820508141">Talegenkendelse</translation>
 <translation id="3482719661246593752"><ph name="ORIGIN" /> kan se følgende filer</translation>
 <translation id="3484273680291419129">Sletter skadelig software...</translation>
 <translation id="3484869148456018791">Få et nyt certifikat</translation>
@@ -2795,6 +2802,7 @@
 <translation id="3640214691812501263">Vil du tilføje "<ph name="EXTENSION_NAME" />" for <ph name="USER_NAME" />?</translation>
 <translation id="3640613767643722554">Lær din assistent at genkende din stemme</translation>
 <translation id="3641456520301071208">Websites kan anmode om adgang til din lokation</translation>
+<translation id="3642070413432681490">Cirkelmarkør</translation>
 <translation id="3642699533549879077">Når en anden person kigger på din skærm, modtager du en underretning, og notifikationsindholdet skjules.</translation>
 <translation id="3645372836428131288">Flyt fingeren en smule for at registrere en anden del af fingeraftrykket.</translation>
 <translation id="3647998456578545569">{COUNT,plural, =1{<ph name="ATTACHMENTS" /> blev modtaget fra <ph name="DEVICE_NAME" />}one{<ph name="ATTACHMENTS" /> received from <ph name="DEVICE_NAME" />}other{<ph name="ATTACHMENTS" /> blev modtaget fra <ph name="DEVICE_NAME" />}}</translation>
@@ -2829,6 +2837,7 @@
 <translation id="368019053277764111">Åbn søgning i sidepanel</translation>
 <translation id="3680683624079082902">Stemme til oplæsning</translation>
 <translation id="3681311097828166361">Tak for din feedback. Du er offline nu, og din rapport sendes senere.</translation>
+<translation id="3681548574519135185">Tastaturfokus</translation>
 <translation id="3682824389861648626">Grænse for bevægelse</translation>
 <translation id="3683524264665795342"><ph name="APP_NAME" /> anmoder om at dele din skærm</translation>
 <translation id="3685598397738512288">Præferencer for USB i Linux</translation>
@@ -3128,6 +3137,7 @@
 <translation id="3948507072814225786"><ph name="ORIGIN" /> kan redigere filer i følgende mapper</translation>
 <translation id="394984172568887996">Importeret fra IE</translation>
 <translation id="3950820424414687140">Log ind</translation>
+<translation id="3950841222883198950">Indtaling</translation>
 <translation id="3953834000574892725">Mine konti</translation>
 <translation id="3954354850384043518">I gang</translation>
 <translation id="3954469006674843813"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (<ph name="REFRESH_RATE" /> hertz)</translation>
@@ -3188,6 +3198,7 @@
 <translation id="3994878504415702912">&amp;Zoom</translation>
 <translation id="3995138139523574647">USB-C-enhed (porten bagpå i højre side)</translation>
 <translation id="3995963973192100066">Afspil animation</translation>
+<translation id="4001540981461989979">Fremhæv musemarkøren ved bevægelse</translation>
 <translation id="4002329649066944389">Administrer websitespecifikke undtagelser</translation>
 <translation id="4002440992267487163">Konfiguration af pinkode</translation>
 <translation id="4005817994523282006">Metode til registrering af tidszone</translation>
@@ -3421,6 +3432,7 @@
 <translation id="4252996741873942488"><ph name="WINDOW_TITLE" /> – faneindholdet deles</translation>
 <translation id="4253168017788158739">Note</translation>
 <translation id="4253183225471855471">Der blev ikke fundet nogen netværk. Indsæt dit SIM-kort, og genstart din enhed, før du prøver igen.</translation>
+<translation id="4254414375763576535">Stor markør</translation>
 <translation id="4254813446494774748">Sprog, der skal oversættes til:</translation>
 <translation id="425573743389990240">Batteriets afladningshastighed i watt (en negativ værdi betyder, at batteriet oplader)</translation>
 <translation id="4256316378292851214">Ge&amp;m video som...</translation>
@@ -3571,6 +3583,7 @@
 <translation id="4400963414856942668">Du kan klikke på stjernen for at tilføje fanen som bogmærke</translation>
 <translation id="4401912261345737180">Opret forbindelse til en enhed ved hjælp af en adgangskode</translation>
 <translation id="4402755511846832236">Bloker websites fra at vide, hvornår du aktivt bruger denne enhed</translation>
+<translation id="4403012369005671154">Indtaling</translation>
 <translation id="4403266582403435904">Du kan til enhver tid nemt gendanne data eller skifte enhed. Backups uploades til Google og krypteres ved hjælp af adgangskoden til dit barns Google-konto.</translation>
 <translation id="4403775189117163360">Vælg en anden mappe</translation>
 <translation id="4404136731284211429">Scan igen</translation>
@@ -3915,6 +3928,7 @@
 <translation id="4733161265940833579"><ph name="BATTERY_PERCENTAGE" /> % (venstre)</translation>
 <translation id="4733793249294335256">Placering</translation>
 <translation id="473546211690256853">Denne konto administreres af <ph name="DOMAIN" /></translation>
+<translation id="4735506354605317060">Cirkelmarkør</translation>
 <translation id="4735803855089279419">Systemet kunne ikke fastslå enheds-id'erne for denne enhed.</translation>
 <translation id="4736292055110123391">Synkroniser dine bogmærker, dine adgangskoder, din historik og meget mere på alle dine enheder</translation>
 <translation id="473775607612524610">Opdater</translation>
@@ -3935,6 +3949,7 @@
 <translation id="4759202969060787081">Åbn ikke</translation>
 <translation id="4759238208242260848">Downloads</translation>
 <translation id="4761104368405085019">Bruge din mikrofon</translation>
+<translation id="4762489666082647806">Markørfarve</translation>
 <translation id="4762718786438001384">Der er næsten ikke mere diskplads på din enhed</translation>
 <translation id="4763408175235639573">Følgende cookies blev gemt, da du fik vist denne side</translation>
 <translation id="4765524037138975789">{MONTHS,plural, =1{Denne enhed gemmes i 1 måned, og du kan oprette forbindelse uden en kode næste gang. Koden indstilles af din administrator.}one{Denne enhed gemmes i {MONTHS} måned, og du kan oprette forbindelse uden en kode næste gang. Koden indstilles af din administrator.}other{Denne enhed gemmes i {MONTHS} måneder, og du kan oprette forbindelse uden en kode næste gang. Koden indstilles af din administrator.}}</translation>
@@ -4030,6 +4045,7 @@
 <translation id="485053257961878904">Synkronisering af notifikationer kunne ikke konfigureres</translation>
 <translation id="4850886885716139402">Vis</translation>
 <translation id="485088796993065002">Websites kan afspille lyd for at levere lyd til musik, videoer og andre medier</translation>
+<translation id="4852916668365817106">Musemarkørens farve</translation>
 <translation id="4853020600495124913">Åbn i &amp;nyt vindue</translation>
 <translation id="4854317507773910281">Vælg en forældrekonto, der skal godkende</translation>
 <translation id="485480310608090163">Flere indstillinger og tilladelser</translation>
@@ -4083,6 +4099,7 @@
 <translation id="4893522937062257019">På låseskærmen</translation>
 <translation id="4897496410259333978">Kontakt din administrator for at få flere oplysninger.</translation>
 <translation id="4898011734382862273">Certifikatet "<ph name="CERTIFICATE_NAME" />" repræsenterer et nøglecenter</translation>
+<translation id="4899052647152077033">Ombyt farver</translation>
 <translation id="4899696330053002588">Indeholder annoncer</translation>
 <translation id="489985760463306091">Du skal genstarte computeren for at afslutte fjernelsen af skadelig software.</translation>
 <translation id="4900392736118574277">Din opstartsside blev ændret til <ph name="URL" />.</translation>
@@ -4289,6 +4306,7 @@
 <translation id="5125751979347152379">Ugyldig webadresse.</translation>
 <translation id="5125967981703109366">Om dette kort</translation>
 <translation id="5126611267288187364">Se ændringer</translation>
+<translation id="5126735406625174440">Konfigurationen er fuldført.</translation>
 <translation id="5127242257756472928">Har ikke tilladelse til at bruge oplysninger om dine skærme til at åbne og placere vinduer</translation>
 <translation id="5127620150973591153">Id for sikker forbindelse: <ph name="TOKEN" /></translation>
 <translation id="5127805178023152808">Synkronisering er slået fra</translation>
@@ -4573,6 +4591,7 @@
 <translation id="5414566801737831689">Læs ikonerne for de websites, du besøger</translation>
 <translation id="5414836363063783498">Bekræfter...</translation>
 <translation id="5417312524372586921">Browsertemaer</translation>
+<translation id="541737483547792035">Forstør skærm</translation>
 <translation id="5419405654816502573">Voice Match</translation>
 <translation id="5420274697768050645">Kræv adgangskode for at låse denne enhed op som ekstra beskyttelse</translation>
 <translation id="5420438158931847627">Bestemmer skarpheden af tekst og billeder</translation>
@@ -4618,6 +4637,7 @@
 <translation id="5459864179070366255">Fortsæt installation</translation>
 <translation id="5461050611724244538">Vi har mistet forbindelsen til din telefon</translation>
 <translation id="5463275305984126951">Indeks over <ph name="LOCATION" /></translation>
+<translation id="5463450804024056231">Tilmeld dig mails vedrørende <ph name="DEVICE_TYPE" /></translation>
 <translation id="5463625433003343978">Finder enheder...</translation>
 <translation id="5463845647891602726">Du kan frigøre plads ved at slette filer fra enheden</translation>
 <translation id="5463856536939868464">Menu med skjulte bogmærker</translation>
@@ -4766,6 +4786,7 @@
 <translation id="5588033542900357244">(<ph name="RATING_COUNT" />)</translation>
 <translation id="558918721941304263">Indlæser apps...</translation>
 <translation id="5592595402373377407">Der er ikke nok data til rådighed endnu.</translation>
+<translation id="5595307023264033512">Samlet lagerplads, der anvendes af websites: <ph name="TOTAL_USAGE" /></translation>
 <translation id="5595485650161345191">Rediger adresse</translation>
 <translation id="5596627076506792578">Flere valgmuligheder</translation>
 <translation id="5600348067066185292">Installationen er nem. Du vil have mulighed for at bekræfte igen, inden der foretages ændringer af din computer.</translation>
@@ -4968,6 +4989,7 @@
 <translation id="5801568494490449797">Præferencer</translation>
 <translation id="5804241973901381774">Tilladelser</translation>
 <translation id="5805697420284793859">Vinduesadministrator</translation>
+<translation id="5806447147478173900">Samlet lagerplads, der bruges af de viste websites: <ph name="TOTAL_USAGE" /></translation>
 <translation id="5806773519584576205">0° (standard)</translation>
 <translation id="5810809306422959727">Denne konto er ikke kvalificeret til forældreindstillinger</translation>
 <translation id="5811614940486072060">Denne fil er ikke downloadet på almindelig vis og kan være skadelig</translation>
@@ -5644,6 +5666,7 @@
 <translation id="6474884162850599008">Fjern tilknytningen af din Google Drev-konto</translation>
 <translation id="6475294023568239942">Du skal frigøre diskplads eller vælge en anden størrelse på Linux-disken i Indstillinger</translation>
 <translation id="6476138569087741884">Zoomniveau i fuld skærm</translation>
+<translation id="6476482583633999078">Talehastighed</translation>
 <translation id="6477822444490674459">Synkronisering af notifikationer understøttes ikke for telefoner med en arbejdsprofil. <ph name="LINK_BEGIN" />Få flere oplysninger<ph name="LINK_END" /></translation>
 <translation id="6478248366783946499">Vil du beholde den farlige fil?</translation>
 <translation id="6480327114083866287">Administreres af <ph name="MANAGER" /></translation>
@@ -5922,6 +5945,7 @@
 <translation id="6750757184909117990">Deaktiver mobilnetværk</translation>
 <translation id="6751344591405861699"><ph name="WINDOW_TITLE" /> (Inkognito)</translation>
 <translation id="6756157672127672536">Appen Filer giver hurtig adgang til filer, som du har gemt i Google Drev, på et eksternt lager eller på din Chrome OS-enhed.</translation>
+<translation id="6756643207511618722">Oplæsningsmaskiner</translation>
 <translation id="6758056191028427665">Fortæl os, hvad du synes.</translation>
 <translation id="6759193508432371551">Gendannelse af fabriksindstillinger</translation>
 <translation id="6761431452438552910">Sørg for, at din Bluetooth-enhed er i parringstilstand og befinder sig tæt på. Par kun med enheder, du har tillid til.</translation>
@@ -6070,6 +6094,7 @@
 <translation id="6889957081990109136">Handlingen er endnu ikke tildelt nogen kontakter</translation>
 <translation id="689007770043972343">Prøv at trække andre åbne faner til din gruppe</translation>
 <translation id="6892812721183419409">Åbn linket som <ph name="USER" /></translation>
+<translation id="6893164346922798247">eSpeak</translation>
 <translation id="6895032998810961280">Rapportér oplysninger til Google om skadelig software, skadelige systemindstillinger og skadelige processer, der blev fundet på din computer i forbindelse med denne oprydning</translation>
 <translation id="6896758677409633944">Kopiér</translation>
 <translation id="6897363604023044284">Vælg de websites, der skal ryddes</translation>
@@ -6137,6 +6162,7 @@
 <translation id="6960507406838246615">Kræver Linux-opdatering</translation>
 <translation id="6960648667961844909">Talefilerne på <ph name="LANGUAGE" /> kunne ikke downloades. Downloaden forsøges senere. Tale sendes til behandling hos Google, indtil downloaden er fuldført.</translation>
 <translation id="696103774840402661">Alle filer og lokale data for alle brugere på denne <ph name="DEVICE_TYPE" /> er blevet slettet permanent.</translation>
+<translation id="6963872466817251924">Fremhævning af tekstmarkør</translation>
 <translation id="6964390816189577014">Helt</translation>
 <translation id="6964760285928603117">Fjern fra gruppe</translation>
 <translation id="6965382102122355670">OK</translation>
@@ -6336,6 +6362,7 @@
 <translation id="7160182524506337403">Du kan nu se din telefons notifikationer</translation>
 <translation id="7163202347044721291">Bekræfter aktiveringskode...</translation>
 <translation id="716640248772308851">"<ph name="EXTENSION" />" har læserettigheder til billed-, video- og lydfiler på de markerede placeringer.</translation>
+<translation id="7167327771183668296">Automatiske klik</translation>
 <translation id="7167486101654761064">&amp;Åbn altid filer af denne type</translation>
 <translation id="716775164025088943">Dine bogmærker, din historik, dine adgangskoder m.m. synkroniseres ikke længere.</translation>
 <translation id="716810439572026343">Downloader <ph name="FILE_NAME" /></translation>
@@ -7217,6 +7244,7 @@
 <translation id="8017176852978888182">Delte indekser i Linux</translation>
 <translation id="8017335670460187064"><ph name="LABEL" /></translation>
 <translation id="8017679124341497925">Genvejen blev redigeret</translation>
+<translation id="8018140635032674020">Åbn appen Udforsk efter konfigurationen for at komme i gang med at spille. Få adgang til hundredvis af de nyeste spil, se spiltilbud, og få en medrivende spiloplevelse.</translation>
 <translation id="8018298733481692628">Vil du slette denne profil?</translation>
 <translation id="8018313076035239964">Kontrollér, hvilke oplysninger websites kan bruge, og hvilket indhold de kan vise dig</translation>
 <translation id="8023133589013344428">Administrer sprog i indstillingerne for ChromeOS Flex</translation>
@@ -7427,6 +7455,7 @@
 <translation id="8213449224684199188">Fototilstand er aktiveret</translation>
 <translation id="8214489666383623925">Åbn fil...</translation>
 <translation id="8215129063232901118">Få adgang til din telefons funktioner via din <ph name="DEVICE_TYPE" /></translation>
+<translation id="8217212468862726597">Fremhæv markør</translation>
 <translation id="8217399928341212914">Fortsæt med at blokere automatiske downloads af flere filer</translation>
 <translation id="822050276545350872">Du skal kun vente lidt endnu</translation>
 <translation id="8221491193165283816">Du blokerer som regel notifikationer. Klik her for at give websitet tilladelse til at sende notifikationer.</translation>
@@ -7665,6 +7694,7 @@
 <translation id="8457451314607652708">Importér bogmærker</translation>
 <translation id="8458341576712814616">Genvej</translation>
 <translation id="8458627787104127436">Åbn alle (<ph name="URL_COUNT" />) i et nyt vindue</translation>
+<translation id="8459940671591713946">Gør klar til at spille</translation>
 <translation id="8460448946170646641">Gennemgå vigtige privatlivs- og sikkerhedsindstillinger</translation>
 <translation id="8460490661223303637">Chrome har fjernet noget indhold for at spare på hukommelsen</translation>
 <translation id="8460932807646981183">Administrer søgemaskiner og websitesøgning</translation>
@@ -7985,6 +8015,7 @@
 <ph name="DOMAIN" /> kræver, at chipkortet er indsat.}}</translation>
 <translation id="8776294611668764629">Din organisation har blokeret denne fil, fordi den er for stor til at gå igennem et sikkerhedstjek. Du kan åbne filer på op til 50 MB.</translation>
 <translation id="8777628254805677039">adgangskoderod</translation>
+<translation id="8778393144535275552">Ryd viste data</translation>
 <translation id="8779944680596936487">Websites kan kun bruge cookies til at se din browseraktivitet på deres eget website</translation>
 <translation id="8780123805589053431">Hent billedbeskrivelser fra Google</translation>
 <translation id="8780443667474968681">Talesøgning er deaktiveret.</translation>
@@ -8463,6 +8494,7 @@
 <translation id="957960681186851048">Dette website har forsøgt at downloade flere filer automatisk</translation>
 <translation id="960987915827980018">Omkring én time tilbage</translation>
 <translation id="962802172452141067">Bogmærkemappe i trævisning</translation>
+<translation id="963000966785016697">Søg på billedet med <ph name="VISUAL_SEARCH_PROVIDER" /></translation>
 <translation id="964286338916298286">Din it-administrator har deaktiveret Chrome Goodies for din enhed.</translation>
 <translation id="964439421054175458">{NUM_APLLICATIONS,plural, =1{App}one{App}other{Apps}}</translation>
 <translation id="964790508619473209">Skærmopstilling</translation>
@@ -8479,6 +8511,7 @@
 <translation id="971774202801778802">Bogmærkets webadresse</translation>
 <translation id="972996901592717370">Sæt fingeren på afbryderknappen. Dine data gemmes sikkert og forlader aldrig din <ph name="DEVICE_TYPE" />.</translation>
 <translation id="973473557718930265">Afslut</translation>
+<translation id="973558314812359997">Musemarkørens størrelse</translation>
 <translation id="975893173032473675">Sprog, der skal oversættes til</translation>
 <translation id="976499800099896273">Dialogboksen for fortrydelse af automatisk rettelse vises for <ph name="TYPED_WORD" />, som er rettet til <ph name="CORRECTED_WORD" />.  Tryk på pil op for at få adgang til rettelsen, tryk på Escape for at ignorere.</translation>
 <translation id="978146274692397928">Den oprindelige tegnsætningsbredde er Fuld</translation>
diff --git a/chrome/app/resources/generated_resources_mn.xtb b/chrome/app/resources/generated_resources_mn.xtb
index 7781165..b35f86e 100644
--- a/chrome/app/resources/generated_resources_mn.xtb
+++ b/chrome/app/resources/generated_resources_mn.xtb
@@ -159,6 +159,7 @@
 <translation id="1148063863818152153">Таны төхөөрөмжийн EID</translation>
 <translation id="1148624853678088576">Бүх зүйл бэлэн боллоо!</translation>
 <translation id="1149401351239820326">Хугацаа дуусах сар</translation>
+<translation id="1149483087970735785">Тусламжийн технологи</translation>
 <translation id="1149725087019908252"><ph name="FILE_NAME" />-г скан хийж байна</translation>
 <translation id="1150490752229770117">Энэ бол энэ <ph name="DEVICE_TYPE" />-н программ хангамж болон аюулгүй байдлын сүүлийн автомат шинэчлэлт. Ирээдүйд шинэчлэлт хийхийн тулд, шинэ загварт дэвшүүлнэ үү. <ph name="LINK_BEGIN" />Нэмэлт мэдээлэл авах<ph name="LINK_END" /></translation>
 <translation id="1150565364351027703">Нарны шил</translation>
@@ -728,6 +729,7 @@
 <translation id="1680849702532889074">Таны Linux аппыг суулгах явцад алдаа гарлаа.</translation>
 <translation id="16815041330799488">Түр санах ойд хуулсан текст, зургийг харах зөвшөөрлийг сайтад хориглох</translation>
 <translation id="1682548588986054654">Шинэ нууцлалтай цонх</translation>
+<translation id="1682696837763999627">Том хулганын курсор</translation>
 <translation id="1682867089915960590">Caret Browsing-г асаах уу?</translation>
 <translation id="1686550358074589746">Гулсуулан бичихийг идэвхжүүлэх</translation>
 <translation id="168715261339224929">Хавчуургаа бүх төхөөрөмждөө авах бол синкийг асаана уу.</translation>
@@ -839,6 +841,7 @@
 <translation id="178092663238929451">Эргэн тойрныхоо хүмүүсээс файл хүлээн авч, тэдэнд илгээхийн тулд Ойролцоо хуваалцах онцлогийг тохируулна уу</translation>
 <translation id="1781291988450150470">Одоогийн ПИН</translation>
 <translation id="1781502536226964113">Шинэ цонхыг нээ</translation>
+<translation id="1781553166608855614">Текстээс яриа</translation>
 <translation id="1781771911845953849">Бүртгэл болон синк</translation>
 <translation id="1781979858217752599">Цонхны аудиог хуваалцах</translation>
 <translation id="1782101999402987960">Шинэчлэлтийг танай администратор блоклосон байна</translation>
@@ -1567,6 +1570,7 @@
 <translation id="244475495405467108">Табуудыг зүүн тийш нь хаах</translation>
 <translation id="2445081178310039857">Өргөтгөлийн стандарт директор шаардлагатай байна.</translation>
 <translation id="2445484935443597917">Шинэ профайл үүсгэх</translation>
+<translation id="2445726032315793326">Хэсэгчилсэн томруулагч</translation>
 <translation id="244641233057214044">Таны хайлттай холбоотой</translation>
 <translation id="2448312741937722512">Төрөл</translation>
 <translation id="2448810255793562605">Сэлгүүрийн автомат скан</translation>
@@ -1859,6 +1863,7 @@
 <translation id="2724841811573117416">WebRTC бүртгэлүүд</translation>
 <translation id="272488616838512378">Нэгж хөрвүүлэлт</translation>
 <translation id="2725200716980197196">Сүлжээний холболтыг дахин сэргээсэн байна.</translation>
+<translation id="272741954544380994"><ph name="VISUAL_SEARCH_PROVIDER" />-р зургийг хайх</translation>
 <translation id="2727633948226935816">Надад дахин битгий сануул</translation>
 <translation id="2727712005121231835">Бодит хэмжээ</translation>
 <translation id="2729314457178420145">Мөн хайлтын өгөгдлийг (<ph name="URL" />) устгах. Ингэснээр таныг Google.com-с гаргаж болзошгүй. <ph name="LEARN_MORE" /></translation>
@@ -2200,6 +2205,7 @@
 <translation id="3030967311408872958">Нар мандахаас нар жаргах хүртэл</translation>
 <translation id="3031417829280473749">Х Агент</translation>
 <translation id="3031557471081358569">Импортолж авах зүйлсийг сонгоно уу:</translation>
+<translation id="3032204772252313646">Автомат тайлбар</translation>
 <translation id="3033348223765101500">Таны өгөгдлийг хянах</translation>
 <translation id="3034627908241330765">Steam-н өөр тохируулгыг ажиллуулж байна. Тохируулгыг дахин ажиллуулахаасаа өмнө үүнийг дуусахыг хүлээнэ үү.</translation>
 <translation id="3036327949511794916">Энэ <ph name="DEVICE_TYPE" />-г буцаах эцсийн хугацаа өнгөрсөн байна.</translation>
@@ -2632,6 +2638,7 @@
 <translation id="3480612136143976912">Шууд тайлбар дээрх тайлбарын хэмжээ болон загварыг өөрчилнө. Зарим апп болон сайт мөн энэ тохиргоог ашиглана.</translation>
 <translation id="3480827850068960424"><ph name="NUM" /> таб оллоо</translation>
 <translation id="3481268647794498892"><ph name="ALTERNATIVE_BROWSER_NAME" />-г <ph name="COUNTDOWN_SECONDS" /> секундийн дараа нээх гэж байна</translation>
+<translation id="348268549820508141">Яриа таних</translation>
 <translation id="3482719661246593752"><ph name="ORIGIN" /> нь дараах файлуудыг үзэх боломжтой</translation>
 <translation id="3484273680291419129">Аюултай программыг устгаж байна...</translation>
 <translation id="3484869148456018791">Шинэ гэрчилгээ авах</translation>
@@ -2790,6 +2797,7 @@
 <translation id="3640214691812501263"><ph name="USER_NAME" />-д зориулсан "<ph name="EXTENSION_NAME" />"-ыг нэмэх үү?</translation>
 <translation id="3640613767643722554">Туслахдаа дуу хоолойгоо таниулна уу</translation>
 <translation id="3641456520301071208">Сайтууд таны байршлыг асуух боломжтой</translation>
+<translation id="3642070413432681490">Курсорыг тодруулах</translation>
 <translation id="3642699533549879077">Өөр хэн нэгэн таны дэлгэц рүү харах үед та сэрэмжлүүлэг авах бөгөөд мэдэгдлийн контентыг нууна.</translation>
 <translation id="3645372836428131288">Хурууны хээг өөр талаас нь авахын тулд хуруугаа бага зэрэг хөдөлгөнө үү</translation>
 <translation id="3647998456578545569">{COUNT,plural, =1{<ph name="DEVICE_NAME" />-с <ph name="ATTACHMENTS" /> хүлээж авсан}other{<ph name="DEVICE_NAME" />-с <ph name="ATTACHMENTS" /> хүлээж авсан}}</translation>
@@ -2824,6 +2832,7 @@
 <translation id="368019053277764111">Хажуугийн самбарт хайлтыг нээнэ үү</translation>
 <translation id="3680683624079082902">Бичвэрийг-ярианд-хувиргах дуу хоолой</translation>
 <translation id="3681311097828166361">Санал хүсэлт илгээсэнд баярлалаа. Та одоогоор офлайн байгаа тул таны тайланг дараа нь илгээх болно.</translation>
+<translation id="3681548574519135185">Гарыг фокуслах</translation>
 <translation id="3682824389861648626">Хөдөлгөөний босго</translation>
 <translation id="3683524264665795342"><ph name="APP_NAME" /> Дэлгэцийг хуваалцах хүсэлт</translation>
 <translation id="3685598397738512288">Linux-н USB сонголт</translation>
@@ -3122,6 +3131,7 @@
 <translation id="3948507072814225786"><ph name="ORIGIN" /> нь дараах фолдерт байгаа файлыг засах боломжтой</translation>
 <translation id="394984172568887996">IE-аас импортолж авсан</translation>
 <translation id="3950820424414687140">Нэвтрэх</translation>
+<translation id="3950841222883198950">Дуу хоолойгоор бичих</translation>
 <translation id="3953834000574892725">Миний бүртгэл</translation>
 <translation id="3954354850384043518">Боловсруулж байна</translation>
 <translation id="3954469006674843813"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (<ph name="REFRESH_RATE" /> Герц)</translation>
@@ -3182,6 +3192,7 @@
 <translation id="3994878504415702912">&amp;Томруулах</translation>
 <translation id="3995138139523574647">USB-C төхөөрөмж (ард талын портын баруун тал)</translation>
 <translation id="3995963973192100066">Анимацийг тоглуулах</translation>
+<translation id="4001540981461989979">Хулганын курсорыг хөдөлж байх үед тодруулах</translation>
 <translation id="4002329649066944389">Сайтын хувьд тодорхойлсон онцгой тохиолдлуудыг удирдах</translation>
 <translation id="4002440992267487163">Пинийн тохируулга</translation>
 <translation id="4005817994523282006">Цагийн бүс илрүүлэх арга</translation>
@@ -3415,6 +3426,7 @@
 <translation id="4252996741873942488"><ph name="WINDOW_TITLE" /> - Табын агуулгыг хуваалцсан</translation>
 <translation id="4253168017788158739">Тайлбар</translation>
 <translation id="4253183225471855471">Сүлжээ олдсонгүй. Дахин оролдохоосоо өмнө SIM-ээ хийгээд, төхөөрөмжөө дахин асаана уу.</translation>
+<translation id="4254414375763576535">Том заагч</translation>
 <translation id="4254813446494774748">Орчуулах хэл:</translation>
 <translation id="425573743389990240">Тэжээл бууралтын хувийг ваттаар (сөрөг утгатай байвал тэжээл цэнэглэж байгааг илтгэнэ)</translation>
 <translation id="4256316378292851214">Видео-г ... гэх хадгалах</translation>
@@ -3565,6 +3577,7 @@
 <translation id="4400963414856942668">Энэ табыг тэмдэглэхийн тулд одыг товшино уу</translation>
 <translation id="4401912261345737180">Дамжуулахын тулд кодоор холбогдоно уу</translation>
 <translation id="4402755511846832236">Сайтуудыг таныг энэ төхөөрөмжийг хэзээ идэвхтэй ашиглаж буйг мэдэхийг нь блоклоно</translation>
+<translation id="4403012369005671154">Яриаг бичвэрт буулгах</translation>
 <translation id="4403266582403435904">Та хүссэн үедээ хялбар аргаар өгөгдлийг сэргээх эсвэл төхөөрөмжүүдийг сэлгэнэ үү. Нөөцлөлтийг Google-д байршуулдаг бөгөөд таны хүүхдийн Google Бүртгэлийн нууц үгийг ашиглан шифрлэдэг.</translation>
 <translation id="4403775189117163360">Өөр фолдер сонгоно уу</translation>
 <translation id="4404136731284211429">Дахин скан хийх</translation>
@@ -3909,6 +3922,7 @@
 <translation id="4733161265940833579"><ph name="BATTERY_PERCENTAGE" />% (Зүүн)</translation>
 <translation id="4733793249294335256">Байршил</translation>
 <translation id="473546211690256853">Энэ дансыг удирдагч <ph name="DOMAIN" /></translation>
+<translation id="4735506354605317060">Заагчийг тодруулах</translation>
 <translation id="4735803855089279419">Систем энэ төхөөрөмжид тохирох төхөөрөмж тодорхойлогчийг тогтоож чадсангүй.</translation>
 <translation id="4736292055110123391">Бүх төхөөрөмждөө хавчуурга, нууц үг, түүх болон бусад зүйлээ синк хийх</translation>
 <translation id="473775607612524610">Шинэчлэх</translation>
@@ -3929,6 +3943,7 @@
 <translation id="4759202969060787081">Бүү нээ</translation>
 <translation id="4759238208242260848">Таталтууд:</translation>
 <translation id="4761104368405085019">Микрофоноо ашигла</translation>
+<translation id="4762489666082647806">Заагчийн өнгө</translation>
 <translation id="4762718786438001384">Төхөөрөмжийн дискний багтаамж маш бага байна</translation>
 <translation id="4763408175235639573">Таныг энэ хуудсыг харахад дараах күүкийг тохируулсан</translation>
 <translation id="4765524037138975789">{MONTHS,plural, =1{Энэ төхөөрөмжийг 1 сарын турш хадгалах бөгөөд та дараагийн удаа кодгүйгээр холбогдох боломжтой. Үүнийг танай администратор тохируулсан.}other{Энэ төхөөрөмжийг {MONTHS} сарын турш хадгалах бөгөөд та дараагийн удаа кодгүйгээр холбогдох боломжтой. Үүнийг танай администратор тохируулсан.}}</translation>
@@ -4024,6 +4039,7 @@
 <translation id="485053257961878904">Мэдэгдэл синк хийхийг тохируулж чадсангүй</translation>
 <translation id="4850886885716139402">Харах</translation>
 <translation id="485088796993065002">Сайтууд хөгжим, видео болон бусад медиад аудио өгөхийн тулд дуу чимээ тоглуулж болзошгүй</translation>
+<translation id="4852916668365817106">Хулганын өнгө</translation>
 <translation id="4853020600495124913">&amp; Шинэ цонхонд нээх</translation>
 <translation id="4854317507773910281">Зөвшөөрөл хүсэх эцэг эхийн бүртгэлийг сонгоно уу</translation>
 <translation id="485480310608090163">Бусад тохиргоо болон зөвшөөрөл</translation>
@@ -4077,6 +4093,7 @@
 <translation id="4893522937062257019">Түгжигдсэн дэлгэцэд</translation>
 <translation id="4897496410259333978">Нэмэлт мэдээлэл авахын тулд администратортайгаа холбогдоно уу.</translation>
 <translation id="4898011734382862273">"<ph name="CERTIFICATE_NAME" />" гэрчилгээ нь Гэрчилгээний бүрэн эрхийг төлөөлж байна</translation>
+<translation id="4899052647152077033">Урвуу өнгө</translation>
 <translation id="4899696330053002588">Зар агуулсан</translation>
 <translation id="489985760463306091">Аюултай программыг устгаж дуусгахын тулд компьютерээ дахин эхлүүлнэ үү</translation>
 <translation id="4900392736118574277">Таны эхлүүлэх хуудсыг <ph name="URL" /> болгож өөрчилсөн.</translation>
@@ -4283,6 +4300,7 @@
 <translation id="5125751979347152379">Хүчингүй холбоос</translation>
 <translation id="5125967981703109366">Энэ картын тухай</translation>
 <translation id="5126611267288187364">Өөрчлөлтийг харах</translation>
+<translation id="5126735406625174440">Тохируулга дууслаа!</translation>
 <translation id="5127242257756472928">Таны дэлгэцийн талаарх мэдээллийг цонх нээх болон байрлуулахад ашиглахыг зөвшөөрөөгүй</translation>
 <translation id="5127620150973591153">Аюулгүй холболтын ID: <ph name="TOKEN" /></translation>
 <translation id="5127805178023152808">Тохиргоог унтраасан байна</translation>
@@ -4567,6 +4585,7 @@
 <translation id="5414566801737831689">Өөрийн ордог вэбсайтуудынхаа дүрснүүдийг уншуулна уу</translation>
 <translation id="5414836363063783498">Баталгаажуулж байна ...</translation>
 <translation id="5417312524372586921">Хөтчийн загвар</translation>
+<translation id="541737483547792035">Дэлгэц томруулах</translation>
 <translation id="5419405654816502573">Voice match</translation>
 <translation id="5420274697768050645">Нэмэлт аюулгүй байдлын үүднээс төхөөрөмжийн түгжээг тайлахын тулд нууц үг оруулахыг шаардана</translation>
 <translation id="5420438158931847627">Текст болон зургийн хурцлалтыг тодорхойлдог</translation>
@@ -4612,6 +4631,7 @@
 <translation id="5459864179070366255">Суулгахын тулд үргэлжлүүлэх</translation>
 <translation id="5461050611724244538">Таны утастай холболт тасарсан</translation>
 <translation id="5463275305984126951"><ph name="LOCATION" />-ын индекс</translation>
+<translation id="5463450804024056231"><ph name="DEVICE_TYPE" /> имэйлүүдэд бүртгүүлнэ үү</translation>
 <translation id="5463625433003343978">Төхөөрөмжүүдийг хайж байна...</translation>
 <translation id="5463845647891602726">Сул зайг нэмэгдүүлэхийн тулд төхөөрөмжөөс файлуудыг устгана уу</translation>
 <translation id="5463856536939868464">Нууцлагдсан хадгалагдсан хуудсуудыг агуулж буй цэс</translation>
@@ -4760,6 +4780,7 @@
 <translation id="5588033542900357244">( <ph name="RATING_COUNT" /> )</translation>
 <translation id="558918721941304263">Аппыг ачаалж байна...</translation>
 <translation id="5592595402373377407">Хангалттай дата байхгүй байна.</translation>
+<translation id="5595307023264033512">Сайтуудын хадгалах сангийн ашиглалтын нийт хэмжээ: <ph name="TOTAL_USAGE" /></translation>
 <translation id="5595485650161345191">Хаягийг засах</translation>
 <translation id="5596627076506792578">Бусад сонголт</translation>
 <translation id="5600348067066185292">Суулгахад цөөн хэдэн хялбар алхам шаардлагатай. Таны компьютерт өөрчлөлт хийхээс өмнө танд баталгаажуулах дахин нэг боломж бий.</translation>
@@ -4963,6 +4984,7 @@
 <translation id="5801568494490449797">Миний сонголт</translation>
 <translation id="5804241973901381774">Зөвшөөрлүүд</translation>
 <translation id="5805697420284793859">Цонхны менежер</translation>
+<translation id="5806447147478173900">Үзүүлсэн сайтуудын хадгалах сангийн ашиглалтын нийт хэмжээ: <ph name="TOTAL_USAGE" /></translation>
 <translation id="5806773519584576205">0° (өгөгдмөл)</translation>
 <translation id="5810809306422959727">Энэ бүртгэл эцэг эхийн хяналтын эрхгүй</translation>
 <translation id="5811614940486072060">Энэ файлыг ихэвчлэн татдаггүй бөгөөд аюултай байж магадгүй</translation>
@@ -5638,6 +5660,7 @@
 <translation id="6474884162850599008">Google Drive хаягийг үл холбох</translation>
 <translation id="6475294023568239942">Дискний зай гаргах эсвэл Linux-н диский хэмжээг Тохиргоо хэсэгт өөрчилнө үү</translation>
 <translation id="6476138569087741884">Бүтэн дэлгэц томруулалтын түвшин</translation>
+<translation id="6476482583633999078">Ярианы хурд</translation>
 <translation id="6477822444490674459">Ажлын профайл дахь утаснуудад мэдэгдэл синк хийхийг дэмждэггүй. <ph name="LINK_BEGIN" />Нэмэлт мэдээлэл авах<ph name="LINK_END" /></translation>
 <translation id="6478248366783946499">Аюултай файлыг хадгалах уу?</translation>
 <translation id="6480327114083866287"><ph name="MANAGER" /> удирддаг</translation>
@@ -5914,6 +5937,7 @@
 <translation id="6750757184909117990">Үүрэн холбоог идэвхгүй болгох</translation>
 <translation id="6751344591405861699"><ph name="WINDOW_TITLE" /> (Нууцлал)</translation>
 <translation id="6756157672127672536">Файлс апп нь таны Google Драйв, гадаад хадгалах сан эсвэл ChromeOS төхөөрөмж дээр хадгалсан файлуудад шуурхай хандалт олгоно.</translation>
+<translation id="6756643207511618722">Ярианы хөдөлгүүр</translation>
 <translation id="6758056191028427665">Биднийг хэр ажиллаж байгааг мэдэгдэнэ үү.</translation>
 <translation id="6759193508432371551">Үйлдвэрийн тохиргоонд нь буцааж оруулах</translation>
 <translation id="6761431452438552910">Bluetooth төхөөрөмжөө хослуулах горимд болон ойролцоо байгаа эсэхийг шалгаарай. Зөвхөн итгэдэг төхөөрөмжүүдтэйгээ хослуулна уу.</translation>
@@ -6062,6 +6086,7 @@
 <translation id="6889957081990109136">Одоогоор сэлгүүр оноогоогүй байна</translation>
 <translation id="689007770043972343">Бусад нээлттэй табыг бүлэг рүүгээ чирч үзнэ үү</translation>
 <translation id="6892812721183419409">Холбоосыг <ph name="USER" />-р нээх</translation>
+<translation id="6893164346922798247">eSpeak</translation>
 <translation id="6895032998810961280">Энэ цэвэрлэгээний үеэр компьютероосоо олдсон хортой программ хангамж, системийн тохиргоо болон боловсруулалтын талаар дэлгэрэнгүй мэдээллийг Google-д мэдээлнэ үү</translation>
 <translation id="6896758677409633944">Хуулах</translation>
 <translation id="6897363604023044284">Устгах сайтуудыг сонгох</translation>
@@ -6129,6 +6154,7 @@
 <translation id="6960507406838246615">Linux-н шинэчлэлт шаардлагатай</translation>
 <translation id="6960648667961844909"><ph name="LANGUAGE" /> дээрх ярианы файлуудыг татаж чадсангүй. Таталтыг дараа оролдоно. Таталтыг дуусах хүртэл яриаг Google-д боловсруулахаар илгээсэн.</translation>
 <translation id="696103774840402661">Энэ <ph name="DEVICE_TYPE" /> дээрх бүх хэрэглэгчийн бүх файл болон дотоод өгөгдлийг бүрмөсөн устгасан.</translation>
+<translation id="6963872466817251924">Текстийн курсор тодруулагч</translation>
 <translation id="6964390816189577014">Баатар</translation>
 <translation id="6964760285928603117">Бүлгээс хасах</translation>
 <translation id="6965382102122355670">OK</translation>
@@ -6328,6 +6354,7 @@
 <translation id="7160182524506337403">Та одоо утасныхаа мэдэгдлүүдийг харах боломжтой</translation>
 <translation id="7163202347044721291">Идэвхжүүлэх кодыг бататгаж байна...</translation>
 <translation id="716640248772308851">"<ph name="EXTENSION" />" нь шалгасан байрлал дахь зураг, видео бичлэг болон дууны файлуудыг уншиж чадна.</translation>
+<translation id="7167327771183668296">Автомат товшилт</translation>
 <translation id="7167486101654761064">&amp; Үргэлж энэ төрлийн файлыг нээх</translation>
 <translation id="716775164025088943">Таны хавчуургууд, түүх, нууц үгнүүд болон бусад зүйлийг цаашид синк хийхгүй.</translation>
 <translation id="716810439572026343"><ph name="FILE_NAME" />-ыг татаж авч байна</translation>
@@ -7207,6 +7234,7 @@
 <translation id="8016266267177410919">Түр хадгалах сан</translation>
 <translation id="8017176852978888182">Linux-н хуваалцсан лавлахууд</translation>
 <translation id="8017679124341497925">Товчлолыг зассан</translation>
+<translation id="8018140635032674020">Тоглож эхлэхийн тулд тохируулсны дараа Explore аппыг нээнэ үү. Сүүлийн үеийн хэдэн зуун тоглоомд нэвтэрч, тоглоомын саналуудыг үзэж, гайхалтай тоглоомын туршлага аваарай.</translation>
 <translation id="8018298733481692628">Энэ профайлыг устгах уу?</translation>
 <translation id="8018313076035239964">Вебсайтын ашиглах боломжтой мэдээлэл болон танд харуулах агуулгыг удирдах</translation>
 <translation id="8023133589013344428">Хэлнүүдийг ChromeOS Flex-н тохиргоонд удирдах</translation>
@@ -7417,6 +7445,7 @@
 <translation id="8213449224684199188">Зургийн горимыг оруулсан</translation>
 <translation id="8214489666383623925">Файлыг нээ...</translation>
 <translation id="8215129063232901118"><ph name="DEVICE_TYPE" />-с утасныхаа чадамжид хандана уу</translation>
+<translation id="8217212468862726597">Заагчийг тодруулах</translation>
 <translation id="8217399928341212914">Олон файл автоматаар зэрэг татах үйлдлийг блозлох тохиргоог үргэлжлүүлэх</translation>
 <translation id="822050276545350872">Үүнээс хойш хүлээх шаардлагагүй болно</translation>
 <translation id="8221491193165283816">Та ихэвчлэн мэдэгдлийг хориглодог. Энэ сайтад танд мэдэгдэхийг зөвшөөрөхийн тулд энд товшино уу.</translation>
@@ -7655,6 +7684,7 @@
 <translation id="8457451314607652708">Хавчуургыг татаж авчрах</translation>
 <translation id="8458341576712814616">Товчлол</translation>
 <translation id="8458627787104127436">Бүгдийг (<ph name="URL_COUNT" />) шинэ цонхонд нээх</translation>
+<translation id="8459940671591713946">Тоглож эхлэхэд бэлдээрэй</translation>
 <translation id="8460448946170646641">Нууцлал болон аюулгүй байдлын үндсэн хяналтуудыг шалгана уу</translation>
 <translation id="8460490661223303637">Дурсамж хадгалахын тулд Chrome зарим контентыг хассан</translation>
 <translation id="8460932807646981183">Хайлтын систем болон сайтын хайлтыг удирдах</translation>
@@ -7971,6 +8001,7 @@
 <ph name="DOMAIN" /> таныг ухаалаг картаа оруулсан хэвээр байхыг шаардана.}}</translation>
 <translation id="8776294611668764629">Энэ файл аюулгүй байдлын шалгалтад хэт том тул танай байгууллага үүнийг блоклосон. Та 50 МБ хүртэлх хэмжээтэй файлуудыг нээх боломжтой.</translation>
 <translation id="8777628254805677039">үндсэн нууц үг</translation>
+<translation id="8778393144535275552">Үзүүлсэн өгөгдлийг арилгах</translation>
 <translation id="8779944680596936487">Сайтууд нь зөвхөн өөрийн сайт дээрх таны хөтчийн үйл ажиллагааг харахын тулд күүки ашиглах боломжтой</translation>
 <translation id="8780123805589053431">Google-с зургийн тайлбар авах</translation>
 <translation id="8780443667474968681">Дуут хайлтыг унтраасан байна.</translation>
@@ -8449,6 +8480,7 @@
 <translation id="957960681186851048">Энэ сайт автоматаар олон файл татахаар оролдсон</translation>
 <translation id="960987915827980018">Ойролцоогоор 1 цаг үлдсэн байна</translation>
 <translation id="962802172452141067">Хавчуургын фолдерын хажуугийн хэсэг</translation>
+<translation id="963000966785016697"><ph name="VISUAL_SEARCH_PROVIDER" />-р зураг хайх</translation>
 <translation id="964286338916298286">Таны IT (МТ) админ таны төхөөрөмжийн Chrome Goodies-г идэвхгүй болгосон байна.</translation>
 <translation id="964439421054175458">{NUM_APLLICATIONS,plural, =1{Апп}other{Аппууд}}</translation>
 <translation id="964790508619473209">Дэлгэцийн зохион байгуулалт</translation>
@@ -8465,6 +8497,7 @@
 <translation id="971774202801778802">Холбоосыг хадгалах</translation>
 <translation id="972996901592717370">Асаах/унтраах товчинд хуруугаараа хүрнэ үү. Таны өгөгдлийг аюулгүй хадгалдаг бөгөөд таны <ph name="DEVICE_TYPE" />-с хэзээ ч гардаггүй.</translation>
 <translation id="973473557718930265">Гарах</translation>
+<translation id="973558314812359997">Хулганын хэмжээ</translation>
 <translation id="975893173032473675">Орчуулах хэл</translation>
 <translation id="976499800099896273"><ph name="TYPED_WORD" /> үгийг <ph name="CORRECTED_WORD" /> болгон автоматаар засахыг болих харилцах цонхыг харуулсан.  Хандахын тулд дээш сумыг, үл хэрэгсэхийн тулд escape-г дарна уу.</translation>
 <translation id="978146274692397928">Тэмдэгтүүдийн анхны өргөн нь Бүтэн</translation>
diff --git a/chrome/app/resources/generated_resources_ne.xtb b/chrome/app/resources/generated_resources_ne.xtb
index 29033ae..b94c5c5 100644
--- a/chrome/app/resources/generated_resources_ne.xtb
+++ b/chrome/app/resources/generated_resources_ne.xtb
@@ -6754,7 +6754,7 @@
 <translation id="7614260613810441905">कुनै साइटले तपाईंको डिभाइसका फाइल वा फोल्डरहरू सम्पादन गर्न खोज्दा सोध्नुहोस् (सिफारिस गरिएको)</translation>
 <translation id="761530003705945209">Google ड्राइभमा ब्याकअप गर्नुहोस् जुनसुकै बेला सजिलैसँग आफ्नो डेटा पुनर्स्थापना गर्नुहोस् वा यन्त्र बदल्नुहोस्। तपाईंको ब्याकअपमा एपको डेटा समावेश हुन्छ। तपाईंका ब्याकअपहरू Google मा अपलोड गरी तपाईंको Google खाताको पासवर्ड प्रयोग गरेर इन्क्रिप्ट गरिन्छन्।</translation>
 <translation id="7615365294369022248">खाता थप्ने क्रममा कुनै त्रुटि भयो</translation>
-<translation id="7616214729753637086">यन्त्र दर्ता गर्दै...</translation>
+<translation id="7616214729753637086">डिभाइस दर्ता गरिँदै छ...</translation>
 <translation id="7617263010641145920">Play Store अन गर्नुहोस्</translation>
 <translation id="7617366389578322136">"<ph name="DEVICE_NAME" />" मा जडान गरिँदै</translation>
 <translation id="7617648809369507487">आवाज नआउने म्यासेजिङ प्रयोग गर्नुहोस्</translation>
diff --git a/chrome/app/resources/generated_resources_uz.xtb b/chrome/app/resources/generated_resources_uz.xtb
index 406058a..6fa0c26 100644
--- a/chrome/app/resources/generated_resources_uz.xtb
+++ b/chrome/app/resources/generated_resources_uz.xtb
@@ -157,6 +157,7 @@
 <translation id="1148063863818152153">Qurilmangiz EID raqami</translation>
 <translation id="1148624853678088576">Tayyor!</translation>
 <translation id="1149401351239820326">Muddati tugaydigan oy</translation>
+<translation id="1149483087970735785">Yordamchi texnologiyalar</translation>
 <translation id="1149725087019908252"><ph name="FILE_NAME" /> tekshirilmoqda</translation>
 <translation id="1150490752229770117">Bu <ph name="DEVICE_TYPE" /> qurilmasidagi DT va xavfsizlik sozlamalari uchun oxirgi avtomatik yangilanish. Keyingi chiqadigan yangilanishlar uchun qurilmaning yangiroq modeli kerak boʻladi. <ph name="LINK_BEGIN" />Batafsil<ph name="LINK_END" /></translation>
 <translation id="1150565364351027703">Qora ko‘zoynak</translation>
@@ -727,6 +728,7 @@
 <translation id="1680849702532889074">Linux ilovasini o‘rnatishda xatolik yuz berdi.</translation>
 <translation id="16815041330799488">Saytlarga klipborddagi rasm va matnlarni ko‘rishni taqiqlash</translation>
 <translation id="1682548588986054654">Yangi inkognito oyna</translation>
+<translation id="1682696837763999627">Katta sichqoncha kursori</translation>
 <translation id="1682867089915960590">Faol kursor rejimi yoqilsinmi?</translation>
 <translation id="1686550358074589746">Qoʻl uzmasdan yozish mumkin</translation>
 <translation id="168715261339224929">Brauzer varaqlarini barcha qurilmalaringizda ko‘rish uchun sinxronizatsiyani yoqing.</translation>
@@ -830,6 +832,7 @@
 <translation id="178092663238929451">Nearby Share funksiyasini sozlang va atrofdagi odamlar bilan fayl almashining</translation>
 <translation id="1781291988450150470">Joriy PIN kod</translation>
 <translation id="1781502536226964113">Yangi varaq sahifasini ochish</translation>
+<translation id="1781553166608855614">Nutq tili</translation>
 <translation id="1781771911845953849">Hisoblar va sinxronizatsiya</translation>
 <translation id="1781979858217752599">Oyna audiosini ulashish</translation>
 <translation id="1782101999402987960">Yangilanishlar administrator tomonidan bloklangan</translation>
@@ -1558,6 +1561,7 @@
 <translation id="244475495405467108">Varaqlarni chapga yopish</translation>
 <translation id="2445081178310039857">Kengaytma tub katalogi talab qilinadi.</translation>
 <translation id="2445484935443597917">Yangi profil yaratish</translation>
+<translation id="2445726032315793326">Qisman kattalashtirish</translation>
 <translation id="244641233057214044">Qidiruvingizga aloqador</translation>
 <translation id="2448312741937722512">Turi</translation>
 <translation id="2448810255793562605">Switch access yordamida avtomatik skanerlash</translation>
@@ -1850,6 +1854,7 @@
 <translation id="2724841811573117416">WebRTC jurnallari</translation>
 <translation id="272488616838512378">Birliklar konvertori</translation>
 <translation id="2725200716980197196">Tarmoq aloqasi tiklandi</translation>
+<translation id="272741954544380994">Rasmni <ph name="VISUAL_SEARCH_PROVIDER" /> orqali qidirish</translation>
 <translation id="2727633948226935816">Boshqa eslatilmasin</translation>
 <translation id="2727712005121231835">Asl hajmi</translation>
 <translation id="2729314457178420145">Shuningdek, brauzer tarixi (<ph name="URL" />) ham tozalansin (Google.com hisobingizdan avtomatik chiqib ketasiz). <ph name="LEARN_MORE" /></translation>
@@ -2191,6 +2196,7 @@
 <translation id="3030967311408872958">Quyosh chiqishidan botishigacha</translation>
 <translation id="3031417829280473749">Sirli josus</translation>
 <translation id="3031557471081358569">Import qilinadigan obyektlarni tanlang:</translation>
+<translation id="3032204772252313646">Avtomatik izohlash</translation>
 <translation id="3033348223765101500">Maʼlumotlaringizni nazorat qiling</translation>
 <translation id="3034627908241330765">Boshqa Steam oʻrnatish jarayoni ishga tushgan. Oʻrnatishni qayta ishga tushirishdan avval boshqa jarayon tugashini kuting.</translation>
 <translation id="3036327949511794916">Bu <ph name="DEVICE_TYPE" /> qurilmasini qayrarish muddati tugadi.</translation>
@@ -2623,6 +2629,7 @@
 <translation id="3480612136143976912">Jonli izoh uchun taglavha hajmi va uslubini sozlang. Ayrim ilovalar va saytlar ham bu sozlamadan foydalanadi.</translation>
 <translation id="3480827850068960424"><ph name="NUM" /> ta varaqda topildi</translation>
 <translation id="3481268647794498892"><ph name="COUNTDOWN_SECONDS" /> soniyadan keyin <ph name="ALTERNATIVE_BROWSER_NAME" /> orqali ochiladi</translation>
+<translation id="348268549820508141">Nutqni aniqlash</translation>
 <translation id="3482719661246593752"><ph name="ORIGIN" /> quyidagi fayllarni koʻra oladi</translation>
 <translation id="3484273680291419129">Zararli dastur o‘chirilmoqda...</translation>
 <translation id="3484869148456018791">Yangi sertifikat olish</translation>
@@ -2781,6 +2788,7 @@
 <translation id="3640214691812501263"><ph name="USER_NAME" /> uchun “<ph name="EXTENSION_NAME" />” kengaytmasi qo‘shilsinmi?</translation>
 <translation id="3640613767643722554">Ovozingizni Assistentga taniting</translation>
 <translation id="3641456520301071208">Saytlar joylashuv axborotingizni talab qilishi mumkin</translation>
+<translation id="3642070413432681490">Kursor halqasi</translation>
 <translation id="3642699533549879077">Kimdir ekraningizga qarasa, bu haqida ogohlantirilasiz va bildirishnoma kontenti berkitiladi.</translation>
 <translation id="3645372836428131288">Barmoqning boshqa qismlari hamq qayd qilinishi uchun uni biroz siljiting.</translation>
 <translation id="3647998456578545569">{COUNT,plural, =1{<ph name="DEVICE_NAME" /> qurilmasidan <ph name="ATTACHMENTS" /> qabul qilindi}other{<ph name="DEVICE_NAME" /> qurilmasidan <ph name="ATTACHMENTS" /> qabul qilindi}}</translation>
@@ -2815,6 +2823,7 @@
 <translation id="368019053277764111">Yon panelda qidiruvni oching</translation>
 <translation id="3680683624079082902">Matnni nutqqa aylantirish ovozi</translation>
 <translation id="3681311097828166361">Fikr-mulohaza uchun rahmat! Hozir internet bilan aloqa yo‘qligi sababli xabaringiz keyinroq yuboriladi.</translation>
+<translation id="3681548574519135185">Fokus chegarasi</translation>
 <translation id="3682824389861648626">Harakat chegarasi</translation>
 <translation id="3683524264665795342"><ph name="APP_NAME" /> ekraningizga ulanmoqchi</translation>
 <translation id="3685598397738512288">Linux tizimida USB sozlamalari</translation>
@@ -3114,6 +3123,7 @@
 <translation id="3948507072814225786"><ph name="ORIGIN" /> quyidagi jildlardagi fayllarni tahrirlay oladi</translation>
 <translation id="394984172568887996">IE brauzeridan import qilinganlar</translation>
 <translation id="3950820424414687140">Kirish</translation>
+<translation id="3950841222883198950">Ovoz bilan yozish</translation>
 <translation id="3953834000574892725">Hisoblarim</translation>
 <translation id="3954354850384043518">Davom etmoqda</translation>
 <translation id="3954469006674843813"><ph name="WIDTH" /> x <ph name="HEIGHT" /> (<ph name="REFRESH_RATE" /> Gs)</translation>
@@ -3174,6 +3184,7 @@
 <translation id="3994878504415702912">&amp;Matn o‘lchami</translation>
 <translation id="3995138139523574647">USB-C qurilma (o‘ng tomondagi orqa port)</translation>
 <translation id="3995963973192100066">Animatsiyani ijro qilish</translation>
+<translation id="4001540981461989979">Sichqoncha kursori harakatlanganda ajratib koʻrsatilsin</translation>
 <translation id="4002329649066944389">Saytga oid istisnolarni boshqarish</translation>
 <translation id="4002440992267487163">PIN-kodni sozlash</translation>
 <translation id="4005817994523282006">Vaqt mintaqasi aniqlanish uslubi</translation>
@@ -3407,6 +3418,7 @@
 <translation id="4252996741873942488"><ph name="WINDOW_TITLE" /> – varaqni ko‘rishga ruxsat berildi</translation>
 <translation id="4253168017788158739">Eslatma</translation>
 <translation id="4253183225471855471">Hech qanday tarmoq topilmadi. SIM kartani joylang va qaytadan urinishdan oldin qurilmani qayta yoqing.</translation>
+<translation id="4254414375763576535">Katta kursor</translation>
 <translation id="4254813446494774748">Qaysi tildan tarjima qilinsin:</translation>
 <translation id="425573743389990240">Batareya quvvatsizlantirish tezligi Vattda (quvvatlanayotganda qiymati teskari ko‘rsatiladi)</translation>
 <translation id="4256316378292851214">Videoni saqlab &amp;olish...</translation>
@@ -3557,6 +3569,7 @@
 <translation id="4400963414856942668">Varaqni bukmarklash uchun yulduzchani bosing</translation>
 <translation id="4401912261345737180">Translatsiya qilish uchun kod yordamida ulaning</translation>
 <translation id="4402755511846832236">Bu qurilmadan foydalanish axborotlarini saytlar bilishini taqiqlash</translation>
+<translation id="4403012369005671154">Nutqdan-matnga</translation>
 <translation id="4403266582403435904">Maʼlumotlarni osongina tiklang yoki istalgan vaqtda qurilmalarni almashtiring. Zaxiralar Google serverlariga yuklanadi va farzandingizning Google hisobingiz paroli bilan shifrlanadi.</translation>
 <translation id="4403775189117163360">Boshqa jildni tanlash</translation>
 <translation id="4404136731284211429">Yana qidirish</translation>
@@ -3901,6 +3914,7 @@
 <translation id="4733161265940833579"><ph name="BATTERY_PERCENTAGE" />% (chap)</translation>
 <translation id="4733793249294335256">Jild</translation>
 <translation id="473546211690256853">Bu hisob <ph name="DOMAIN" /> tomonidan boshqariladi</translation>
+<translation id="4735506354605317060">Halqali korsor</translation>
 <translation id="4735803855089279419">Qurilma identifikatorlari aniqlanmadi.</translation>
 <translation id="4736292055110123391">Bukmarklar, tarix va parollaringizni barcha qurilmalaringizda sinxronlang</translation>
 <translation id="473775607612524610">Yangilash</translation>
@@ -3921,6 +3935,7 @@
 <translation id="4759202969060787081">Ochilmasin</translation>
 <translation id="4759238208242260848">Yuklanmalar</translation>
 <translation id="4761104368405085019">Mikrofoningizni ishlatish</translation>
+<translation id="4762489666082647806">Kursor rangi</translation>
 <translation id="4762718786438001384">Qurilma diskida bo‘sh joy deyarli qolmadi</translation>
 <translation id="4763408175235639573">Bu sahifaga tashrif buyurish vaqtida kompyuteringizga quyidagi cookie fayllari saqlandi:</translation>
 <translation id="4765524037138975789">{MONTHS,plural, =1{Bu qurilma 1 oy davomida saqlanadi va unga keyingi safar kodsiz ulanish mumkin. Bu administrator tomonidan sozlangan.}other{Bu qurilma {MONTHS} oy davomida saqlanadi va unga keyingi safar kodsiz ulanish mumkin. Bu administrator tomonidan sozlangan.}}</translation>
@@ -4016,6 +4031,7 @@
 <translation id="485053257961878904">Bildirishnomalar sinxronizatsiyasi sozlanmadi</translation>
 <translation id="4850886885716139402">Ko‘rish</translation>
 <translation id="485088796993065002">Saytlar musiqa, video va boshqa media kontent audiosini chiqarish uchun tovushlarni ijro qilishi mumkin</translation>
+<translation id="4852916668365817106">Sichqoncha rangi</translation>
 <translation id="4853020600495124913">Yangi oynada &amp;ochish</translation>
 <translation id="4854317507773910281">Ruxsat olish uchun ota-onangiz hisobini tanlang</translation>
 <translation id="485480310608090163">Yana sozlamalar va ruxsatlar</translation>
@@ -4069,6 +4085,7 @@
 <translation id="4893522937062257019">Qulflangan ekranda</translation>
 <translation id="4897496410259333978">Batafsil axborot uchun administratorga murojaat qiling.</translation>
 <translation id="4898011734382862273">“<ph name="CERTIFICATE_NAME" />” sertifikati Sertifikatlash markazi tomonidan berilgan</translation>
+<translation id="4899052647152077033">Teskari ranglar</translation>
 <translation id="4899696330053002588">Reklama mavjud</translation>
 <translation id="489985760463306091">Zararli dasturlardan tozalashni yakunlash uchun kompyuterni o‘chirib yoqing</translation>
 <translation id="4900392736118574277">Boshlang‘ich sahifa <ph name="URL" /> manziliga o‘zgartirildi.</translation>
@@ -4275,6 +4292,7 @@
 <translation id="5125751979347152379">Yaroqsiz URL manzil.</translation>
 <translation id="5125967981703109366">Bu kartochka haqida</translation>
 <translation id="5126611267288187364">Oʻzgarishlar</translation>
+<translation id="5126735406625174440">Sozlandi!</translation>
 <translation id="5127242257756472928">Oynalarni ochish va joylash uchun ekrandagi axborotdan foydalana olmaydi</translation>
 <translation id="5127620150973591153">Xavfsiz ulanish identifikatori: <ph name="TOKEN" /></translation>
 <translation id="5127805178023152808">Sinxronizatsiya o‘chiq</translation>
@@ -4559,6 +4577,7 @@
 <translation id="5414566801737831689">Tashrif buyurilgan saytlar ikonkalariga ruxsat</translation>
 <translation id="5414836363063783498">Tekshirilmoqda...</translation>
 <translation id="5417312524372586921">Brauzer mavzulari</translation>
+<translation id="541737483547792035">Ekranni kattalashtirish</translation>
 <translation id="5419405654816502573">Voice Match</translation>
 <translation id="5420274697768050645">Xavfsizlikni oshirish uchun qurilma qulfi parol bilan ochilsin</translation>
 <translation id="5420438158931847627">Matn va rasmlarning keskinligini aniqlaydi</translation>
@@ -4604,6 +4623,7 @@
 <translation id="5459864179070366255">Oʻrnatishda davom etish</translation>
 <translation id="5461050611724244538">Telefon bilan ulanish uzilib qoldi</translation>
 <translation id="5463275305984126951"><ph name="LOCATION" /> tarkibi</translation>
+<translation id="5463450804024056231"><ph name="DEVICE_TYPE" /> xatlariga yozilish</translation>
 <translation id="5463625433003343978">Qurilmalar qidirilmoqda...</translation>
 <translation id="5463845647891602726">Boʻsh joyni koʻpaytirish uchun qurilmangizdan fayllarni oʻchiring</translation>
 <translation id="5463856536939868464">Yashirin xatcho‘plari mavjud menyu</translation>
@@ -4752,6 +4772,7 @@
 <translation id="5588033542900357244">(<ph name="RATING_COUNT" />)</translation>
 <translation id="558918721941304263">Ilovalar yuklanmoqda…</translation>
 <translation id="5592595402373377407">Ma’lumotlar yetarli emas.</translation>
+<translation id="5595307023264033512">Saytlar ishlatgan jami xotira: <ph name="TOTAL_USAGE" /></translation>
 <translation id="5595485650161345191">Manzilni o‘zgartirish</translation>
 <translation id="5596627076506792578">Boshqa sozlamalar</translation>
 <translation id="5600348067066185292">Oʻrnatish jarayoni bir nechta bosqichni oʻz ichiga oladi. Kompyuterda oʻzgartirishlar amalga oshishidan oldin tasdiqlash uchun yana boshqa imkoniyat boʻladi.</translation>
@@ -4954,6 +4975,7 @@
 <translation id="5801568494490449797">Sozlamalar</translation>
 <translation id="5804241973901381774">Ruxsatlar</translation>
 <translation id="5805697420284793859">Oynalar menejeri</translation>
+<translation id="5806447147478173900">Ochilgan saytlar ishlatgan jami xotira: <ph name="TOTAL_USAGE" /></translation>
 <translation id="5806773519584576205">0° (asosiy)</translation>
 <translation id="5810809306422959727">Bu hisobdan ota-ona nazorati uchun foydalanish mumkin emas.</translation>
 <translation id="5811614940486072060">Bu faylni yuklab olish tavsiya etilmaydi, chunki u zararli bo‘lishi mumkin</translation>
@@ -5630,6 +5652,7 @@
 <translation id="6474884162850599008">Google Drive hisobini uzib qo‘yish</translation>
 <translation id="6475294023568239942">Sozlamalar orqali Linux diski hajmini oʻzgartiring yoki xotiradan joy boʻshating</translation>
 <translation id="6476138569087741884">Butun ekran masshtabi</translation>
+<translation id="6476482583633999078">Nutq tezligi</translation>
 <translation id="6477822444490674459">Ish profilidagi telefonlarda bildirishnomalar sinxronlanmaydi. <ph name="LINK_BEGIN" />Batafsil<ph name="LINK_END" /></translation>
 <translation id="6478248366783946499">Xavfli fayl yuklab olinsinmi?</translation>
 <translation id="6480327114083866287"><ph name="MANAGER" /> tomonidan boshqariladi</translation>
@@ -5906,6 +5929,7 @@
 <translation id="6750757184909117990">Mobil tarmoqni faolsizlantirish</translation>
 <translation id="6751344591405861699"><ph name="WINDOW_TITLE" /> (Inkognito)</translation>
 <translation id="6756157672127672536">Fayllar ilovasi yordamida Google Drive, ChromeOS qurilmalari yoki tashqi xotira qurilmasidagi hujjatlarni tezkor ochish mumkin.</translation>
+<translation id="6756643207511618722">Nutq sintezatorlari</translation>
 <translation id="6758056191028427665">Bizga fikr-mulohaza yuboring.</translation>
 <translation id="6759193508432371551">Zavod sozlamalariga qaytarish</translation>
 <translation id="6761431452438552910">Bluetooth qurilmasi ulanish rejimida va yaqin-atrofdaligini tekshiring. Faqat ishonchli qurilmalarga ulang.</translation>
@@ -6054,6 +6078,7 @@
 <translation id="6889957081990109136">Kalit hali belgilanmagan</translation>
 <translation id="689007770043972343">Boshqa ochiq varaqlarni guruhlarga tortib tashlash mumkin</translation>
 <translation id="6892812721183419409">Havolani <ph name="USER" /> nomidan ochish</translation>
+<translation id="6893164346922798247">eSpeak</translation>
 <translation id="6895032998810961280">Tozalash vaqtida bu kompyuterda topilgan zararli dasturlar, tizim sozlamalari va jarayonlar hisoboti Google serverlariga yuborilsin</translation>
 <translation id="6896758677409633944">Nusxa olish</translation>
 <translation id="6897363604023044284">Tozalash uchun saytlarni tanlang</translation>
@@ -6121,6 +6146,7 @@
 <translation id="6960507406838246615">Linuxni yangilash zarur</translation>
 <translation id="6960648667961844909"><ph name="LANGUAGE" /> nutq fayllari yuklab olinmadi. Keyinroq yuklab olinadi. Yuklab olinguncha nutq qayta ishlanishi uchun Googlega yuborildi.</translation>
 <translation id="696103774840402661">Ushbu <ph name="DEVICE_TYPE" /> qurilmasidagi barcha fayllar va foydalanuvchi maʼlumotlari butunlay tozalandi.</translation>
+<translation id="6963872466817251924">Matn kursorini ajratib belgilash</translation>
 <translation id="6964390816189577014">Super qahramon</translation>
 <translation id="6964760285928603117">Guruhdan chiqarish</translation>
 <translation id="6965382102122355670">OK</translation>
@@ -6320,6 +6346,7 @@
 <translation id="7160182524506337403">Endi siz telefoningizdagi bildirishnomalarni koʻra olasiz</translation>
 <translation id="7163202347044721291">Aktivatsiya kodi tasdiqlanmoqda…</translation>
 <translation id="716640248772308851">“<ph name="EXTENSION" />” kengaytmasi belgilangan jildlardan rasm, audio va video fayllarni o‘qishi mumkin.</translation>
+<translation id="7167327771183668296">Avtomatik kliklar</translation>
 <translation id="7167486101654761064">&amp;Bu turdagi fayllar har doim ochilsin</translation>
 <translation id="716775164025088943">Endi bukmarklar, tarix, parollar va boshqa maʼlumotlaringiz sinxronlanmaydi.</translation>
 <translation id="716810439572026343"><ph name="FILE_NAME" /> yuklab olinmoqda</translation>
@@ -7200,6 +7227,7 @@
 <translation id="8016266267177410919">Vaqtinchalik saqlash jildi</translation>
 <translation id="8017176852978888182">Linux umumiy kataloglari</translation>
 <translation id="8017679124341497925">Yorliq tahrirlandi</translation>
+<translation id="8018140635032674020">Oʻyinlarni boshlash uchun sozlanganidan keyin Tanishuv ilovasini oching. Yuzlab eng oxirgi oʻyinlarga ruxsat oling, oʻyinlarga oid takliflar bilan tanishing va oʻyinlardan unutilmas taassurot oling.</translation>
 <translation id="8018298733481692628">Bu profil oʻchirilsinmi?</translation>
 <translation id="8018313076035239964">Qanday saytlarni sizga ko‘rsatish mumkinligi va ularni sizning qanday ma’lumotlaringizdan foydalanishini nazorat qilish</translation>
 <translation id="8023133589013344428">Tillarni ChromeOS Flex sozlamalaridan boshqaring</translation>
@@ -7410,6 +7438,7 @@
 <translation id="8213449224684199188">Foto rejimi yoqildi</translation>
 <translation id="8214489666383623925">Faylni ochish...</translation>
 <translation id="8215129063232901118"><ph name="DEVICE_TYPE" /> orqali telefoningizning imkoniyatlaridan foydalaning</translation>
+<translation id="8217212468862726597">Kursorni ajratib belgilash</translation>
 <translation id="8217399928341212914">Bir necha fayllarni birdaniga avtomatik yuklab olishga hali ham ruxsat berilsin</translation>
 <translation id="822050276545350872">Bundan buyon kutish shart emas</translation>
 <translation id="8221491193165283816">Odatda bildirishnomalarni bloklaysiz. Bu saytga ruxsat berish uchun bu yerga bosing.</translation>
@@ -7648,6 +7677,7 @@
 <translation id="8457451314607652708">Xatcho‘plarni import qilish</translation>
 <translation id="8458341576712814616">Buyruq</translation>
 <translation id="8458627787104127436">Barchasini (<ph name="URL_COUNT" />) yangi oynalarda ochish</translation>
+<translation id="8459940671591713946">Oʻyinlarni hoziroq boshlang</translation>
 <translation id="8460448946170646641">Asosiy maxfiylik va xavfsizlik boshqaruv elementlarini tekshirish</translation>
 <translation id="8460490661223303637">Xotiradan joy ochish uchun Chrome ayrim kontentni tozaladi</translation>
 <translation id="8460932807646981183">Qidiruv vositalari va sayt qidiruvini boshqarish</translation>
@@ -7966,6 +7996,7 @@
 <ph name="DOMAIN" /> smart karta kiritilishini talab qilmoqda.}}</translation>
 <translation id="8776294611668764629">Xavfsizlik tekshiruvi uchun hajmi juda kattaligi uchun tashkilotingiz bu faylni bloklagan. 50 MB gacha fayllarni tekshirish mumkin.</translation>
 <translation id="8777628254805677039">superfoydalanuvchi paroli</translation>
+<translation id="8778393144535275552">Ochilgan maʼlumotlarni tozalash</translation>
 <translation id="8779944680596936487">Saytlar cookie fayllarni faqatgina u saytni kezish vaqtingizda koʻradi</translation>
 <translation id="8780123805589053431">Rasmlarni tavsiflash (Google orqali)</translation>
 <translation id="8780443667474968681">Ovozli qidiruv o‘chiq.</translation>
@@ -8444,6 +8475,7 @@
 <translation id="957960681186851048">Bu sayt bir necha fayllarni avtomatik yuklab olishga urinib ko‘rdi</translation>
 <translation id="960987915827980018">1 soatcha qoldi</translation>
 <translation id="962802172452141067">Xatcho‘plar javoni</translation>
+<translation id="963000966785016697">Rasmni <ph name="VISUAL_SEARCH_PROVIDER" /> orqali qidirish</translation>
 <translation id="964286338916298286">IT administratoringiz qurilmangiz uchun Chrome bonuslarini failsizlantirgan.</translation>
 <translation id="964439421054175458">{NUM_APLLICATIONS,plural, =1{Ilova}other{Ilovalar}}</translation>
 <translation id="964790508619473209">Ekran joylashuv tartibi</translation>
@@ -8460,6 +8492,7 @@
 <translation id="971774202801778802">Xatcho‘p URL manzili</translation>
 <translation id="972996901592717370">Quvvat tugmasiga barmoq bilan tegining. Maʼlumotlaringiz himoya ostida va faqat <ph name="DEVICE_TYPE" /> qurilmangizda saqlanadi.</translation>
 <translation id="973473557718930265">Chiqish</translation>
+<translation id="973558314812359997">Sichqoncha hajmi</translation>
 <translation id="975893173032473675">Tarjima qilinadigan til</translation>
 <translation id="976499800099896273"><ph name="TYPED_WORD" /> soʻzini <ph name="CORRECTED_WORD" /> soʻziga avtomatik tuzatishni bekor qilish oynasi chiqarildi.  Ochish uchun tepaga, inkor qilish uchun escape tugmasini bosing.</translation>
 <translation id="978146274692397928">Imlo belgilarining boshlang‘ich kengligi - to‘liq</translation>
diff --git a/chrome/app/resources/generated_resources_zh-HK.xtb b/chrome/app/resources/generated_resources_zh-HK.xtb
index 4934f7e..b03654d7 100644
--- a/chrome/app/resources/generated_resources_zh-HK.xtb
+++ b/chrome/app/resources/generated_resources_zh-HK.xtb
@@ -1558,7 +1558,7 @@
 <translation id="2433836460518180625">只解鎖裝置</translation>
 <translation id="2434449159125086437">無法設定打印機,請檢查設定並再試一次。</translation>
 <translation id="2434758125294431199">選取誰可以與您分享檔案</translation>
-<translation id="2434915728183570229">你現在可以查看手機的應用程式</translation>
+<translation id="2434915728183570229">您現可查看手機的應用程式</translation>
 <translation id="2435137177546457207">Google Chrome 和 ChromeOS Flex 的《附加條款》</translation>
 <translation id="2435248616906486374">網絡已中斷連線</translation>
 <translation id="2435457462613246316">顯示密碼</translation>
@@ -4240,7 +4240,7 @@
 <translation id="5072900412896857127">無法載入《Google Play 服務條款》。請檢查您的網絡連線,然後再試一次。</translation>
 <translation id="5073956501367595100">{0,plural,offset:2 =1{<ph name="FILE1" />}=2{<ph name="FILE1" />、<ph name="FILE2" />}other{<ph name="FILE1" />、<ph name="FILE2" /> 和另外 # 個檔案}}</translation>
 <translation id="5074318175948309511">您可能需要重新載入這個網頁,新設定才會生效。</translation>
-<translation id="5074761966806028321">仍需要權限才能完成設定</translation>
+<translation id="5074761966806028321">仍需權限才能完成設定</translation>
 <translation id="5075910247684008552">系統預設會封鎖安全網站上的不安全內容</translation>
 <translation id="5078638979202084724">將所有分頁加入書籤</translation>
 <translation id="5078796286268621944">PIN 不正確</translation>
@@ -4605,7 +4605,7 @@
 <translation id="5449551289610225147">密碼無效</translation>
 <translation id="5449588825071916739">將所有分頁加入書籤</translation>
 <translation id="5449716055534515760">關閉視窗(&amp;D)</translation>
-<translation id="5452446625764825792">你現在可以查看手機最近的相片、媒體和應用程式</translation>
+<translation id="5452446625764825792">您現可查看手機最近的相片、媒體和應用程式</translation>
 <translation id="5452976525201205853"><ph name="LANGUAGE" /> (可離線使用)</translation>
 <translation id="5454166040603940656">與 <ph name="PROVIDER" /></translation>
 <translation id="545484289444831485">查看更多搜尋結果</translation>
@@ -4666,7 +4666,7 @@
 <translation id="5499211612787418966">目前冇突顯到呢個對話框。㩒一下 Alt、Shift 同 A 鍵就可以突顯呢個對話框。</translation>
 <translation id="5499313591153584299">這個檔案可能對您的電腦有害。</translation>
 <translation id="5499453227627332024">您的 Linux 容器有可用的升級。您亦可稍後從「設定」應用程式進行升級。</translation>
-<translation id="5499476581866658341">你現在可以查看手機近期的相片和媒體</translation>
+<translation id="5499476581866658341">您現可查看手機最近的相片和媒體</translation>
 <translation id="549957179819296104">新圖示</translation>
 <translation id="5500168250243071806">在您登入後,<ph name="BEGIN_LINK_SEARCH" />搜尋記錄<ph name="END_LINK_SEARCH" />和<ph name="BEGIN_LINK_GOOGLE" />其他形式的活動<ph name="END_LINK_GOOGLE" />可能會儲存至您的 Google 帳戶。您可以隨時刪除這些資料。</translation>
 <translation id="5500709606820808700">今天已執行安全檢查</translation>
@@ -6328,7 +6328,7 @@
 <translation id="7152478047064750137">這個擴充程式不需要任何特殊權限</translation>
 <translation id="7154130902455071009">將您的起始網頁變更為:<ph name="START_PAGE" /></translation>
 <translation id="7155171745945906037">相機或檔案中的現有照片</translation>
-<translation id="7160182524506337403">你現在可以查看手機的通知</translation>
+<translation id="7160182524506337403">您現可查看手機的通知</translation>
 <translation id="7163202347044721291">正在驗證啟動碼…</translation>
 <translation id="716640248772308851">「<ph name="EXTENSION" />」對於勾選位置中的圖片、影片和音效檔案具有讀取權限。</translation>
 <translation id="7167486101654761064">保持開啟這類檔案(&amp;A)</translation>
@@ -7345,7 +7345,7 @@
 <translation id="8138997515734480534"><ph name="VM_NAME" /> 狀態</translation>
 <translation id="8139447493436036221">「Google 雲端硬碟」檔案</translation>
 <translation id="8140070492745508800"><ph name="FIRST_DEVICE" />、<ph name="SECOND_DEVICE" /></translation>
-<translation id="8141418916163800697">你可以前往 Phone Hub 設定調整更多功能</translation>
+<translation id="8141418916163800697">您可在 Phone Hub 設定中設定更多功能</translation>
 <translation id="8141584439523427891">正在替代瀏覽器中開啟</translation>
 <translation id="8141725884565838206">管理您的密碼</translation>
 <translation id="814204052173971714">{COUNT,plural, =1{1 部影片}other{# 部影片}}</translation>
@@ -7553,7 +7553,7 @@
 <translation id="833986336429795709">如要開啟此連結,請選擇應用程式</translation>
 <translation id="8342221978608739536">沒有試過</translation>
 <translation id="8342861492835240085">選取相片集</translation>
-<translation id="8345848587667658367">你現在可以查看手機最近的相片、媒體、通知和應用程式</translation>
+<translation id="8345848587667658367">您現可查看手機最近的相片、媒體、通知和應用程式</translation>
 <translation id="8347227221149377169">列印工作</translation>
 <translation id="834785183489258869">在無痕模式中,網站不能使用 Cookie 查看您在各網站的瀏覽活動,例如放送個人化廣告。部分網站的功能可能會無法正常運作。</translation>
 <translation id="8350789879725387295">Dock 中的觸控筆工具</translation>
@@ -8159,7 +8159,7 @@
 <translation id="8957757410289731985">自訂個人檔案</translation>
 <translation id="895944840846194039">JavaScript 記憶體使用量</translation>
 <translation id="8960208913905765425">「快速解答」的單位轉換</translation>
-<translation id="8960638196855923532">你現在可以查看手機的通知和應用程式</translation>
+<translation id="8960638196855923532">您現可查看手機的通知和應用程式</translation>
 <translation id="8962051932294470566">一次只能分享一個檔案,請在目前的傳輸完成後再試一次。</translation>
 <translation id="8962083179518285172">隱藏詳情</translation>
 <translation id="8962918469425892674">此網站正在使用動作或光線感應器。</translation>
@@ -8311,7 +8311,7 @@
 <translation id="9103868373786083162">按下即可返回上一頁,內容選單會顯示記錄</translation>
 <translation id="9108035152087032312">為視窗命名…(&amp;W)</translation>
 <translation id="9108072915170399168">目前嘅數據用量設定係「唔用互聯網」</translation>
-<translation id="9108294543511800041">你現在可以查看手機最近的相片、媒體和通知</translation>
+<translation id="9108294543511800041">您現可查看手機最近的相片、媒體和應用程式</translation>
 <translation id="9108674852930645435">探索 <ph name="DEVICE_TYPE" /> 的新功能</translation>
 <translation id="9108808586816295166">可能無法隨時使用安全 DNS</translation>
 <translation id="9109122242323516435">如要釋出空間,請刪除裝置儲存空間內的檔案。</translation>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 3806a1b..21d738c7 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3422,6 +3422,9 @@
   <message name="IDS_SETTINGS_COOKIES_CACHE_STORAGE" desc="The text shown when there is Cache Storage (name of a Web standard) in the Cookies table">
     Cache Storage
   </message>
+  <message name="IDS_SETTINGS_COOKIES_QUOTA_STORAGE" desc="The text shown when there is Quota Managed Storage in the Cookies table">
+    Quota-managed storage
+  </message>
 
   <!-- Sync / People Page -->
   <message name="IDS_SETTINGS_PEOPLE" desc="Name of the settings page which manages the user's relationship to Google.">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_COOKIES_QUOTA_STORAGE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_COOKIES_QUOTA_STORAGE.png.sha1
new file mode 100644
index 0000000..22b2021a
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_COOKIES_QUOTA_STORAGE.png.sha1
@@ -0,0 +1 @@
+a15ec5fbec4e9cbe4f170edbd44d5e79d0857de5
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2849f33..e9f0512c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -974,6 +974,8 @@
     "page_load_metrics/observers/formfill_page_load_metrics_observer.h",
     "page_load_metrics/observers/from_gws_page_load_metrics_observer.cc",
     "page_load_metrics/observers/from_gws_page_load_metrics_observer.h",
+    "page_load_metrics/observers/gws_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/gws_page_load_metrics_observer.h",
     "page_load_metrics/observers/histogram_suffixes.cc",
     "page_load_metrics/observers/histogram_suffixes.h",
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 56246b11..98eb3c5 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -241,6 +241,7 @@
 #include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chromeos/ash/components/assistant/buildflags.h"
+#include "chromeos/ash/components/memory/swap_configuration.h"
 #include "chromeos/ash/services/assistant/public/cpp/features.h"
 #include "components/app_restore/features.h"
 #include "components/metrics/structured/structured_metrics_features.h"  // nogncheck
@@ -3491,6 +3492,9 @@
     {"enable-notifications-revamp", flag_descriptions::kNotificationsRevampName,
      flag_descriptions::kNotificationsRevampDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kNotificationsRefresh)},
+    {"enable-zram-writeback", flag_descriptions::kEnableZramWriteback,
+     flag_descriptions::kEnableZramWritebackDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::kCrOSEnableZramWriteback)},
     // Used to carry the policy value crossing the Chrome process lifetime.
     {crosapi::browser_util::kLacrosAvailabilityPolicyInternalName, "", "",
      kOsCrOS, MULTI_VALUE_TYPE(kLacrosAvailabilityPolicyChoices)},
diff --git a/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS b/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS
index aef9c2c4..24cfb61 100644
--- a/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS
+++ b/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS
@@ -1 +1,2 @@
 hendrich@chromium.org
+mpetrisor@chromium.org
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index b0244bec..f67d440c 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -92,7 +92,7 @@
 const int kInputTextViewMetricValue = 1;
 
 static const char* kEnglishDictationCommands[] = {
-    "delete",
+    "delete the previous character",
     "move to the previous character",
     "move to the next character",
     "move to the previous line",
@@ -105,7 +105,18 @@
     "select all",
     "unselect",
     "help",
-    "new line"};
+    "new line",
+    "cancel",
+    "delete the previous word",
+    "delete the previous sentence",
+    "move to the next word",
+    "move to the previous word",
+    "delete phrase",
+    "replace phrase with another phrase",
+    "insert phrase before another phrase",
+    "select from phrase to another phrase",
+    "move to the next sentence",
+    "move to the previous sentence"};
 
 PrefService* GetActiveUserPrefs() {
   return ProfileManager::GetActiveUserProfile()->GetPrefs();
@@ -746,10 +757,12 @@
 IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeleteCharacter) {
   SendFinalResultAndWaitForTextAreaValue("Vega", "Vega");
   // Capitalization and whitespace shouldn't matter.
-  SendFinalResultAndWaitForTextAreaValue(" Delete", "Veg");
-  SendFinalResultAndWaitForTextAreaValue("delete ", "Ve");
-  SendFinalResultAndWaitForTextAreaValue("  delete ", "V");
-  SendFinalResultAndWaitForTextAreaValue("DELETE", "");
+  SendFinalResultAndWaitForTextAreaValue(" Delete the previous character",
+                                         "Veg");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous character", "Ve");
+  SendFinalResultAndWaitForTextAreaValue("  delete the previous character",
+                                         "V");
+  SendFinalResultAndWaitForTextAreaValue("DELETE the previous character", "");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationCommandsTest, MoveByCharacter) {
@@ -790,7 +803,7 @@
   SendFinalResultAndWaitForTextAreaValue("Vega is the brightest star in Lyra",
                                          "Vega is the brightest star in Lyra");
   SendFinalResultAndWaitForSelectionChanged("Select all", web_contents);
-  SendFinalResultAndWaitForTextAreaValue("delete", "");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous character", "");
   SendFinalResultAndWaitForTextAreaValue(
       "Vega is the fifth brightest star in the sky",
       "Vega is the fifth brightest star in the sky");
@@ -852,6 +865,172 @@
   WaitForRecognitionStopped();
 }
 
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, StopListening) {
+  SendFinalResultAndWait("cancel");
+  WaitForRecognitionStopped();
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevWordSimple) {
+  SendFinalResultAndWaitForTextAreaValue("This is a test", "This is a test");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous word",
+                                         "This is a ");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevWordExtraSpace) {
+  SendFinalResultAndWaitForTextAreaValue("This is a test ", "This is a test ");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous word",
+                                         "This is a ");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevWordNewLine) {
+  SendFinalResultAndWaitForTextAreaValue("This is a test\n\n",
+                                         "This is a test\n\n");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous word",
+                                         "This is a test\n");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevWordPunctuation) {
+  SendFinalResultAndWaitForTextAreaValue("This.is.a.test. ",
+                                         "This.is.a.test. ");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous word",
+                                         "This.is.a.test");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevWordMiddleOfWord) {
+  SendFinalResultAndWaitForTextAreaValue("This is a test.", "This is a test.");
+  // Move the text caret into the middle of the word "test".
+  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
+  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous word",
+                                         "This is a t.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevSentSimple) {
+  SendFinalResultAndWaitForTextAreaValue("Hello, world.", "Hello, world.");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous sentence", "");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevSentWhiteSpace) {
+  SendFinalResultAndWaitForTextAreaValue("  \nHello, world.\n  ",
+                                         "  \nHello, world.\n  ");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous sentence", "");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevSentPunctuation) {
+  SendFinalResultAndWaitForTextAreaValue(
+      "Hello, world! Good afternoon; good evening? Goodnight, world.",
+      "Hello, world! Good afternoon; good evening? Goodnight, world.");
+  SendFinalResultAndWaitForTextAreaValue(
+      "delete the previous sentence",
+      "Hello, world! Good afternoon; good evening?");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous sentence",
+                                         "Hello, world! Good afternoon;");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous sentence",
+                                         "Hello, world!");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevSentTwoSentences) {
+  SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
+                                         "Hello, world. Goodnight, world.");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous sentence",
+                                         "Hello, world.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, DeletePrevSentMiddleOfSentence) {
+  SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
+                                         "Hello, world. Goodnight, world.");
+  // Move the text caret into the middle of the second sentence.
+  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
+  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
+  SendFinalResultAndWaitForTextAreaValue("delete the previous sentence",
+                                         "Hello, world.d.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, MoveByWord) {
+  SendFinalResultAndWaitForTextAreaValue("This is a quiz", "This is a quiz");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the previous word");
+  SendFinalResultAndWaitForTextAreaValue("pop ", "This is a pop quiz");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the next word");
+  SendFinalResultAndWaitForTextAreaValue("folks!", "This is a pop quiz folks!");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, SmartDeletePhraseSimple) {
+  SendFinalResultAndWaitForTextAreaValue("This is a difficult test",
+                                         "This is a difficult test");
+  SendFinalResultAndWaitForTextAreaValue("delete difficult", "This is a test");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest,
+                       SmartDeletePhraseCaseInsensitive) {
+  SendFinalResultAndWaitForTextAreaValue("This is a DIFFICULT test",
+                                         "This is a DIFFICULT test");
+  SendFinalResultAndWaitForTextAreaValue("delete difficult", "This is a test");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest,
+                       SmartDeletePhraseDuplicateMatches) {
+  SendFinalResultAndWaitForTextAreaValue("The cow jumped over the moon.",
+                                         "The cow jumped over the moon.");
+  // Deletes the right-most occurrence of "the".
+  SendFinalResultAndWaitForTextAreaValue("delete the",
+                                         "The cow jumped over moon.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest,
+                       SmartDeletePhraseDeletesLeftOfCaret) {
+  SendFinalResultAndWaitForTextAreaValue("The cow jumped over the moon.",
+                                         "The cow jumped over the moon.");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the previous word");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the previous word");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the previous word");
+  SendFinalResultAndWaitForTextAreaValue("delete the",
+                                         "cow jumped over the moon.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest,
+                       SmartDeletePhraseDeletesAtWordBoundaries) {
+  SendFinalResultAndWaitForTextAreaValue("A square is also a rectangle.",
+                                         "A square is also a rectangle.");
+  // Deletes the first word "a", not the first character "a".
+  SendFinalResultAndWaitForTextAreaValue("delete a",
+                                         "A square is also rectangle.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, SmartReplacePhrase) {
+  SendFinalResultAndWaitForTextAreaValue("This is a difficult test.",
+                                         "This is a difficult test.");
+  SendFinalResultAndWaitForTextAreaValue("replace difficult with simple",
+                                         "This is a simple test.");
+  SendFinalResultAndWaitForTextAreaValue("replace is with isn't",
+                                         "This isn't a simple test.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, SmartInsertBefore) {
+  SendFinalResultAndWaitForTextAreaValue("This is a test.", "This is a test.");
+  SendFinalResultAndWaitForTextAreaValue("insert simple before test",
+                                         "This is a simple test.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, SmartSelectBetween) {
+  SendFinalResultAndWaitForTextAreaValue("This is a test.", "This is a test.");
+  SendFinalResultAndWaitForSelectionChanged(
+      "select from this to test",
+      browser()->tab_strip_model()->GetActiveWebContents());
+  SendFinalResultAndWaitForTextAreaValue("Hello world", "Hello world.");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationCommandsTest, MoveBySentence) {
+  SendFinalResultAndWaitForTextAreaValue("Hello world! Goodnight world?",
+                                         "Hello world! Goodnight world?");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the previous sentence");
+  SendFinalResultAndWaitForTextAreaValue(
+      "Good evening.", "Hello world! Good evening. Goodnight world?");
+  SendFinalResultAndWaitForCaretBoundsChanged("move to the next sentence");
+  SendFinalResultAndWaitForTextAreaValue(
+      "Time for a midnight snack",
+      "Hello world! Good evening. Goodnight world? Time for a midnight snack");
+}
+
 // Tests the behavior of the Dictation bubble UI.
 class DictationUITest : public DictationTest {
  protected:
@@ -1234,227 +1413,7 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(
-    Network,
-    DictationHiddenMacrosTest,
-    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
-
-INSTANTIATE_TEST_SUITE_P(
-    OnDevice,
-    DictationHiddenMacrosTest,
-    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, StopListening) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  RunHiddenMacro(/*STOP_LISTENING*/ 16);
-  WaitForRecognitionStopped();
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevWordSimple) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a test", "This is a test");
-  RunHiddenMacro(/*DELETE_PREV_WORD*/ 17);
-  WaitForTextAreaValue("This is a ");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevWordExtraSpace) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a test ", "This is a test ");
-  RunHiddenMacro(/*DELETE_PREV_WORD*/ 17);
-  WaitForTextAreaValue("This is a ");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevWordNewLine) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a test\n\n",
-                                         "This is a test\n\n");
-  RunHiddenMacro(/*DELETE_PREV_WORD*/ 17);
-  WaitForTextAreaValue("This is a test\n");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevWordPunctuation) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This.is.a.test. ",
-                                         "This.is.a.test. ");
-  RunHiddenMacro(/*DELETE_PREV_WORD*/ 17);
-  WaitForTextAreaValue("This.is.a.test");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevWordMiddleOfWord) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a test.", "This is a test.");
-  // Move the text caret into the middle of the word "test".
-  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
-  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
-  RunHiddenMacro(/*DELETE_PREV_WORD*/ 17);
-  WaitForTextAreaValue("This is a t.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentSimple) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("Hello, world.", "Hello, world.");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentWhiteSpace) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("  \nHello, world.\n  ",
-                                         "  \nHello, world.\n  ");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentPunctuation) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue(
-      "Hello, world! Good afternoon; good evening? Goodnight, world.",
-      "Hello, world! Good afternoon; good evening? Goodnight, world.");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world! Good afternoon; good evening?");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world! Good afternoon;");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world!");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentTwoSentences) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
-                                         "Hello, world. Goodnight, world.");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
-                       DeletePrevSentMiddleOfSentence) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
-                                         "Hello, world. Goodnight, world.");
-  // Move the text caret into the middle of the second sentence.
-  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
-  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
-  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world.d.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, MoveByWord) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a quiz", "This is a quiz");
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_PREV_WORD*/ 20);
-  SendFinalResultAndWaitForTextAreaValue("pop ", "This is a pop quiz");
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_NEXT_WORD*/ 19);
-  SendFinalResultAndWaitForTextAreaValue("folks!", "This is a pop quiz folks!");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, SmartDeletePhraseSimple) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a difficult test",
-                                         "This is a difficult test");
-  RunHiddenMacroWithStringArg(/* SMART_DELETE_PHRASE */ 21, "difficult");
-  WaitForTextAreaValue("This is a test");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
-                       SmartDeletePhraseCaseInsensitive) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a DIFFICULT test",
-                                         "This is a DIFFICULT test");
-  RunHiddenMacroWithStringArg(/* SMART_DELETE_PHRASE */ 21, "difficult");
-  WaitForTextAreaValue("This is a test");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
-                       SmartDeletePhraseDuplicateMatches) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("The cow jumped over the moon.",
-                                         "The cow jumped over the moon.");
-  // Deletes the right-most occurrence of "the".
-  RunHiddenMacroWithStringArg(/* SMART_DELETE_PHRASE */ 21, "the");
-  WaitForTextAreaValue("The cow jumped over moon.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
-                       SmartDeletePhraseDeletesLeftOfCaret) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("The cow jumped over the moon.",
-                                         "The cow jumped over the moon.");
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_PREV_WORD*/ 20);
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_PREV_WORD*/ 20);
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_PREV_WORD*/ 20);
-  RunHiddenMacroWithStringArg(/* SMART_DELETE_PHRASE */ 21, "the");
-  WaitForTextAreaValue("cow jumped over the moon.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
-                       SmartDeletePhraseDeletesAtWordBoundaries) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("A square is also a rectangle.",
-                                         "A square is also a rectangle.");
-  // Deletes the first word "a", not the first character "a".
-  RunHiddenMacroWithStringArg(/* SMART_DELETE_PHRASE */ 21, "a");
-  WaitForTextAreaValue("A square is also rectangle.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, SmartReplacePhrase) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a difficult test.",
-                                         "This is a difficult test.");
-  RunHiddenMacroWithTwoStringArgs(/* SMART_REPLACE_PHRASE */ 22, "difficult",
-                                  "simple");
-  WaitForTextAreaValue("This is a simple test.");
-  RunHiddenMacroWithTwoStringArgs(/*SMART_REPLACE_PHRASE*/ 22, "is", "isn't");
-  WaitForTextAreaValue("This isn't a simple test.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, SmartInsertBefore) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a test.", "This is a test.");
-  RunHiddenMacroWithTwoStringArgs(/* SMART_INSERT_BEFORE */ 23, "simple",
-                                  "test");
-  WaitForTextAreaValue("This is a simple test.");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, SmartSelectBetween) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("This is a test.", "This is a test.");
-  RunSmartSelectMacroAndWaitForSelectionChanged("is", "test");
-  SendFinalResultAndWaitForTextAreaValue("delete", "This .");
-}
-
-IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, MoveBySentence) {
-  ToggleDictationWithKeystroke();
-  WaitForRecognitionStarted();
-  SendFinalResultAndWaitForTextAreaValue("Hello world! Goodnight world?",
-                                         "Hello world! Goodnight world?");
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_PREV_SENT*/ 26);
-  SendFinalResultAndWaitForTextAreaValue(
-      "Good evening.", "Hello world! Good evening. Goodnight world?");
-  RunMacroAndWaitForCaretBoundsChanged(/*NAV_NEXT_SENT*/ 25);
-  SendFinalResultAndWaitForTextAreaValue(
-      "Time for a midnight snack",
-      "Hello world! Good evening. Goodnight world? Time for a midnight snack");
-}
+// Add tests for hidden macros below.
 
 // Tests behavior of Dictation and installation of Pumpkin.
 class DictationPumpkinInstallTest : public DictationTest {
diff --git a/chrome/browser/ash/app_restore/full_restore_service_unittest.cc b/chrome/browser/ash/app_restore/full_restore_service_unittest.cc
index fac89ba7..82935f93 100644
--- a/chrome/browser/ash/app_restore/full_restore_service_unittest.cc
+++ b/chrome/browser/ash/app_restore/full_restore_service_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ash/app_restore/full_restore_service.h"
 
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
@@ -81,12 +80,9 @@
 
 syncer::SyncData CreateRestoreAppsAndPagesPrefSyncData(RestoreOption value) {
   sync_pb::EntitySpecifics specifics;
-  sync_pb::PreferenceSpecifics* pref =
-      features::IsSyncSettingsCategorizationEnabled()
-          ? specifics.mutable_os_preference()->mutable_preference()
-          : specifics.mutable_preference();
   SetPrefValue(kRestoreAppsAndPagesPrefName,
-               base::Value(static_cast<int>(value)), pref);
+               base::Value(static_cast<int>(value)),
+               specifics.mutable_os_preference()->mutable_preference());
   return syncer::SyncData::CreateRemoteData(
       specifics, syncer::ClientTagHash::FromHashed("unused"));
 }
@@ -219,10 +215,7 @@
           std::make_unique<syncer::FakeSyncChangeProcessor>(),
           std::make_unique<syncer::SyncErrorFactoryMock>());
 
-      if (!features::IsSyncSettingsCategorizationEnabled())
-        return;
-      // If SyncSettingsCategorization is enabled, OS_PREFERENCES sync should be
-      // started separately.
+      // OS_PREFERENCES sync should be started separately.
       syncer::SyncableService* os_sync_service =
           profile()->GetTestingPrefService()->GetSyncableService(
               syncer::OS_PREFERENCES);
@@ -233,21 +226,16 @@
       return;
     }
 
-    if (features::IsSyncSettingsCategorizationEnabled()) {
-      syncer::SyncDataList os_sync_data_list;
-      os_sync_data_list.push_back(CreateRestoreAppsAndPagesPrefSyncData(
-          maybe_restore_apps_and_pages_value.value()));
-      syncer::SyncableService* os_sync_service =
-          profile()->GetTestingPrefService()->GetSyncableService(
-              syncer::OS_PREFERENCES);
-      os_sync_service->MergeDataAndStartSyncing(
-          syncer::OS_PREFERENCES, os_sync_data_list,
-          std::make_unique<syncer::FakeSyncChangeProcessor>(),
-          std::make_unique<syncer::SyncErrorFactoryMock>());
-    } else {
-      sync_data_list.push_back(CreateRestoreAppsAndPagesPrefSyncData(
-          maybe_restore_apps_and_pages_value.value()));
-    }
+    syncer::SyncDataList os_sync_data_list;
+    os_sync_data_list.push_back(CreateRestoreAppsAndPagesPrefSyncData(
+        maybe_restore_apps_and_pages_value.value()));
+    syncer::SyncableService* os_sync_service =
+        profile()->GetTestingPrefService()->GetSyncableService(
+            syncer::OS_PREFERENCES);
+    os_sync_service->MergeDataAndStartSyncing(
+        syncer::OS_PREFERENCES, os_sync_data_list,
+        std::make_unique<syncer::FakeSyncChangeProcessor>(),
+        std::make_unique<syncer::SyncErrorFactoryMock>());
 
     sync_service->MergeDataAndStartSyncing(
         syncer::PREFERENCES, sync_data_list,
@@ -271,11 +259,9 @@
     os_change_list.push_back(syncer::SyncChange(
         FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
         CreateRestoreAppsAndPagesPrefSyncData(restore_apps_and_pages_value)));
-    syncer::ModelType model_type =
-        features::IsSyncSettingsCategorizationEnabled() ? syncer::OS_PREFERENCES
-                                                        : syncer::PREFERENCES;
     syncer::SyncableService* os_sync_service =
-        profile()->GetTestingPrefService()->GetSyncableService(model_type);
+        profile()->GetTestingPrefService()->GetSyncableService(
+            syncer::OS_PREFERENCES);
     os_sync_service->ProcessSyncChanges(FROM_HERE, os_change_list);
   }
 
diff --git a/chrome/browser/ash/browser_context_keyed_service_factories.cc b/chrome/browser/ash/browser_context_keyed_service_factories.cc
index 37b0feb..a010a35d 100644
--- a/chrome/browser/ash/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/ash/browser_context_keyed_service_factories.cc
@@ -39,13 +39,13 @@
 #include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
 #include "chrome/browser/chromeos/extensions/input_method_api.h"
 #include "chrome/browser/chromeos/extensions/media_player_api.h"
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.h"
 #include "chrome/browser/chromeos/fileapi/file_change_service_factory.h"
 #include "chrome/browser/ui/ash/calendar/calendar_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
 
 #if defined(USE_CUPS)
 #include "chrome/browser/ash/printing/cups_proxy_service_manager_factory.h"
+#include "chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.h"
 #include "chrome/browser/extensions/api/printing/printing_api_handler.h"
 #endif
 
@@ -74,7 +74,7 @@
   extensions::MediaPlayerAPI::GetFactoryInstance();
 #if defined(USE_CUPS)
   extensions::PrintingAPIHandler::GetFactoryInstance();
-  extensions::PrintJobFinishedEventDispatcher::GetFactoryInstance();
+  extensions::PrintingMetricsService::GetFactoryInstance();
 #endif
   FileChangeServiceFactory::GetInstance();
   file_manager::EventRouterFactory::GetInstance();
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index 15ba286b..ec6f328 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -1379,6 +1379,11 @@
   zram_detail_ = base::MakeRefCounted<memory::ZramMetrics>();
   zram_detail_->Start();
 
+  if (ash::memory::ZramWritebackController::IsSupportedAndEnabled()) {
+    zram_writeback_controller_ = ash::memory::ZramWritebackController::Create();
+    zram_writeback_controller_->Start();
+  }
+
   ChromeBrowserMainPartsLinux::PostBrowserStart();
 }
 
@@ -1396,6 +1401,10 @@
     zram_detail_->Stop();
   }
 
+  if (zram_writeback_controller_ != nullptr) {
+    zram_writeback_controller_->Stop();
+  }
+
   SystemProxyManager::Shutdown();
   device_activity_controller_.reset();
   crostini_unsupported_action_notifier_.reset();
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.h b/chrome/browser/ash/chrome_browser_main_parts_ash.h
index 2145005..b00d956 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.h
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/chrome_browser_main_linux.h"
 #include "chrome/browser/memory/memory_kills_monitor.h"
 #include "chromeos/ash/components/memory/memory.h"
+#include "chromeos/ash/components/memory/zram_writeback_controller.h"
 // TODO(https://crbug.com/1164001): remove and use forward declaration.
 #include "chromeos/network/fast_transition_observer.h"
 
@@ -285,6 +286,9 @@
 
   std::unique_ptr<AudioSurveyHandler> audio_survey_handler_;
 
+  std::unique_ptr<ash::memory::ZramWritebackController>
+      zram_writeback_controller_;
+
   // Only temporarily owned, will be null after PostCreateMainMessageLoop().
   // The Accessor is constructed before initialization of FeatureList and should
   // only be used by ChromeFeaturesServiceProvider.
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 4264ddc..97c1031 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/ui.gni")
+import("//printing/buildflags/buildflags.gni")
 
 assert(is_chromeos_ash)
 assert(use_ozone)
@@ -209,6 +210,15 @@
     "window_util.h",
   ]
 
+  if (use_cups) {
+    sources += [
+      "print_job_info_idl_conversions.cc",
+      "print_job_info_idl_conversions.h",
+      "printing_metrics_ash.cc",
+      "printing_metrics_ash.h",
+    ]
+  }
+
   deps = [
     "//ash",
     "//ash/components/account_manager",
@@ -368,6 +378,10 @@
     "test_mojo_connection_manager_unittest.cc",
   ]
 
+  if (use_cups) {
+    sources += [ "print_job_info_idl_conversions_unittest.cc" ]
+  }
+
   deps = [
     ":crosapi",
     ":test_support",
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc
index 6e7d88d9..aef0cea 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.cc
+++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -125,6 +125,10 @@
 #include "media/mojo/services/stable_video_decoder_factory_service.h"
 #endif  // BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC)
 
+#if defined(USE_CUPS)
+#include "chrome/browser/ash/crosapi/printing_metrics_ash.h"
+#endif  // defined(USE_CUPS)
+
 namespace crosapi {
 namespace {
 
@@ -206,6 +210,9 @@
       prefs_ash_(
           std::make_unique<PrefsAsh>(g_browser_process->profile_manager(),
                                      g_browser_process->local_state())),
+#if defined(USE_CUPS)
+      printing_metrics_ash_(std::make_unique<PrintingMetricsAsh>()),
+#endif  // defined(USE_CUPS)
       remoting_ash_(std::make_unique<RemotingAsh>()),
       resource_manager_ash_(std::make_unique<ResourceManagerAsh>()),
       screen_manager_ash_(std::make_unique<ScreenManagerAsh>()),
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h
index c13b7f7d..46bf122 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.h
+++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -17,6 +17,7 @@
 #include "mojo/public/cpp/bindings/generic_pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
+#include "printing/buildflags/buildflags.h"
 
 #if BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC)
 namespace media {
@@ -76,6 +77,7 @@
 class PolicyServiceAsh;
 class PowerAsh;
 class PrefsAsh;
+class PrintingMetricsAsh;
 class RemotingAsh;
 class ResourceManagerAsh;
 class ScreenManagerAsh;
@@ -326,6 +328,12 @@
     return chrome_app_kiosk_service_ash_.get();
   }
 
+#if defined(USE_CUPS)
+  PrintingMetricsAsh* printing_metrics_ash() {
+    return printing_metrics_ash_.get();
+  }
+#endif  // defined(USE_CUPS)
+
   SearchProviderAsh* search_provider_ash() {
     return search_provider_ash_.get();
   }
@@ -444,6 +452,9 @@
   std::unique_ptr<PolicyServiceAsh> policy_service_ash_;
   std::unique_ptr<PowerAsh> power_ash_;
   std::unique_ptr<PrefsAsh> prefs_ash_;
+#if defined(USE_CUPS)
+  std::unique_ptr<PrintingMetricsAsh> printing_metrics_ash_;
+#endif  // defined(USE_CUPS)
   std::unique_ptr<RemotingAsh> remoting_ash_;
   std::unique_ptr<ResourceManagerAsh> resource_manager_ash_;
   std::unique_ptr<ScreenManagerAsh> screen_manager_ash_;
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.cc b/chrome/browser/ash/crosapi/print_job_info_idl_conversions.cc
similarity index 98%
rename from chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.cc
rename to chrome/browser/ash/crosapi/print_job_info_idl_conversions.cc
index 73efd26..33ce215 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.cc
+++ b/chrome/browser/ash/crosapi/print_job_info_idl_conversions.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h"
+#include "chrome/browser/ash/crosapi/print_job_info_idl_conversions.h"
 
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h b/chrome/browser/ash/crosapi/print_job_info_idl_conversions.h
similarity index 62%
rename from chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h
rename to chrome/browser/ash/crosapi/print_job_info_idl_conversions.h
index bacbcb5..d90ee62 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h
+++ b/chrome/browser/ash/crosapi/print_job_info_idl_conversions.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINT_JOB_INFO_IDL_CONVERSIONS_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINT_JOB_INFO_IDL_CONVERSIONS_H_
+#ifndef CHROME_BROWSER_ASH_CROSAPI_PRINT_JOB_INFO_IDL_CONVERSIONS_H_
+#define CHROME_BROWSER_ASH_CROSAPI_PRINT_JOB_INFO_IDL_CONVERSIONS_H_
 
 #include "chrome/browser/ash/printing/history/print_job_info.pb.h"
 #include "chrome/common/extensions/api/printing_metrics.h"
@@ -15,4 +15,4 @@
 
 }  // namespace extensions
 
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINT_JOB_INFO_IDL_CONVERSIONS_H_
+#endif  // CHROME_BROWSER_ASH_CROSAPI_PRINT_JOB_INFO_IDL_CONVERSIONS_H_
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions_unittest.cc b/chrome/browser/ash/crosapi/print_job_info_idl_conversions_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions_unittest.cc
rename to chrome/browser/ash/crosapi/print_job_info_idl_conversions_unittest.cc
index 484a455..eb8363a 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions_unittest.cc
+++ b/chrome/browser/ash/crosapi/print_job_info_idl_conversions_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h"
+#include "chrome/browser/ash/crosapi/print_job_info_idl_conversions.h"
 
 #include "chrome/browser/extensions/api/printing/printing_api.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/crosapi/printing_metrics_ash.cc b/chrome/browser/ash/crosapi/printing_metrics_ash.cc
new file mode 100644
index 0000000..1e585b18c
--- /dev/null
+++ b/chrome/browser/ash/crosapi/printing_metrics_ash.cc
@@ -0,0 +1,92 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/crosapi/printing_metrics_ash.h"
+
+#include "base/bind.h"
+#include "base/notreached.h"
+#include "chrome/browser/ash/crosapi/print_job_info_idl_conversions.h"
+#include "chrome/browser/ash/printing/history/print_job_history_service.h"
+#include "chrome/browser/ash/printing/history/print_job_history_service_factory.h"
+#include "chrome/browser/ash/printing/history/print_job_info.pb.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+
+namespace crosapi {
+
+PrintingMetricsForProfileAsh::PrintingMetricsForProfileAsh(
+    Profile* profile,
+    mojo::PendingRemote<crosapi::mojom::PrintJobObserverForProfile> observer)
+    : profile_(profile), observer_(std::move(observer)) {
+  auto* history_service =
+      ash::PrintJobHistoryServiceFactory::GetForBrowserContext(profile_);
+  // The print job history service is not available on the lock screen.
+  if (history_service) {
+    print_job_history_service_observation_.Observe(history_service);
+  }
+}
+
+PrintingMetricsForProfileAsh::~PrintingMetricsForProfileAsh() = default;
+
+void PrintingMetricsForProfileAsh::GetPrintJobs(GetPrintJobsCallback callback) {
+  ash::PrintJobHistoryServiceFactory::GetForBrowserContext(profile_)
+      ->GetPrintJobs(
+          base::BindOnce(&PrintingMetricsForProfileAsh::OnPrintJobsRetrieved,
+                         weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void PrintingMetricsForProfileAsh::OnPrintJobFinished(
+    const ash::printing::proto::PrintJobInfo& print_job_info) {
+  auto dict_value =
+      extensions::PrintJobInfoProtoToIdl(print_job_info).ToValue();
+  observer_->OnPrintJobFinished(std::move(*dict_value));
+}
+
+void PrintingMetricsForProfileAsh::OnShutdown() {
+  // ash::PrintJobHistoryService might go out of scope earlier than the ash
+  // service since we don't declare any factory dependencies here. Therefore
+  // it's safer to reset the observer at this point.
+  print_job_history_service_observation_.Reset();
+}
+
+void PrintingMetricsForProfileAsh::OnPrintJobsRetrieved(
+    GetPrintJobsCallback callback,
+    bool success,
+    std::vector<ash::printing::proto::PrintJobInfo> print_job_infos) {
+  if (!success) {
+    std::move(callback).Run(/*print_jobs=*/{});
+    return;
+  }
+
+  std::vector<base::Value> print_job_info_values;
+  for (const auto& print_job_info : print_job_infos) {
+    auto dict_value =
+        extensions::PrintJobInfoProtoToIdl(print_job_info).ToValue();
+    print_job_info_values.emplace_back(std::move(*dict_value));
+  }
+
+  std::move(callback).Run(std::move(print_job_info_values));
+}
+
+PrintingMetricsAsh::PrintingMetricsAsh() = default;
+PrintingMetricsAsh::~PrintingMetricsAsh() = default;
+
+void PrintingMetricsAsh::RegisterForMainProfile(
+    mojo::PendingReceiver<crosapi::mojom::PrintingMetricsForProfile> receiver,
+    mojo::PendingRemote<crosapi::mojom::PrintJobObserverForProfile> observer) {
+  RegisterForProfile(ProfileManager::GetPrimaryUserProfile(),
+                     std::move(receiver), std::move(observer));
+}
+
+void PrintingMetricsAsh::RegisterForProfile(
+    Profile* profile,
+    mojo::PendingReceiver<crosapi::mojom::PrintingMetricsForProfile> receiver,
+    mojo::PendingRemote<crosapi::mojom::PrintJobObserverForProfile> observer) {
+  DCHECK(profile);
+  auto service = std::make_unique<PrintingMetricsForProfileAsh>(
+      profile, std::move(observer));
+  receivers_.Add(std::move(service), std::move(receiver));
+}
+
+}  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/printing_metrics_ash.h b/chrome/browser/ash/crosapi/printing_metrics_ash.h
new file mode 100644
index 0000000..d0aed37
--- /dev/null
+++ b/chrome/browser/ash/crosapi/printing_metrics_ash.h
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_CROSAPI_PRINTING_METRICS_ASH_H_
+#define CHROME_BROWSER_ASH_CROSAPI_PRINTING_METRICS_ASH_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/ash/printing/history/print_job_history_service.h"
+#include "chrome/browser/ash/printing/history/print_job_info.pb.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/crosapi/mojom/printing_metrics.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/unique_receiver_set.h"
+
+namespace crosapi {
+
+// Ash implementation of crosapi::mojom::PrintingMetricsForProfile.
+// This class communicates with ash::PrintJobHistory service for the given
+// profile -- queries print jobs and listens to finished events.
+class PrintingMetricsForProfileAsh
+    : public crosapi::mojom::PrintingMetricsForProfile,
+      public ash::PrintJobHistoryService::Observer {
+ public:
+  PrintingMetricsForProfileAsh(
+      Profile* profile,
+      mojo::PendingRemote<crosapi::mojom::PrintJobObserverForProfile> observer);
+  ~PrintingMetricsForProfileAsh() override;
+
+  // crosapi::mojom::PrintingMetricsForProfile:
+  void GetPrintJobs(GetPrintJobsCallback) override;
+
+  // ash::PrintJobHistoryService::Observer:
+  void OnPrintJobFinished(
+      const ash::printing::proto::PrintJobInfo& print_job_info) override;
+  void OnShutdown() override;
+
+ private:
+  void OnPrintJobsRetrieved(GetPrintJobsCallback,
+                            bool success,
+                            std::vector<ash::printing::proto::PrintJobInfo>);
+
+  raw_ptr<Profile> profile_ = nullptr;
+
+  base::ScopedObservation<ash::PrintJobHistoryService,
+                          ash::PrintJobHistoryService::Observer>
+      print_job_history_service_observation_{this};
+
+  mojo::Remote<crosapi::mojom::PrintJobObserverForProfile> observer_;
+
+  base::WeakPtrFactory<PrintingMetricsForProfileAsh> weak_factory_{this};
+};
+
+// Ash implementation for crosapi::mojom::PrintingMetrics.
+// This class creates bridges between ash and lacros -- the actual
+// processing happens in PrintingMetricsForProfileAsh.
+class PrintingMetricsAsh : public crosapi::mojom::PrintingMetrics {
+ public:
+  PrintingMetricsAsh();
+  ~PrintingMetricsAsh() override;
+
+  // crosapi::mojom::PrintingMetrics:
+  void RegisterForMainProfile(
+      mojo::PendingReceiver<crosapi::mojom::PrintingMetricsForProfile> receiver,
+      mojo::PendingRemote<crosapi::mojom::PrintJobObserverForProfile> observer)
+      override;
+
+  void RegisterForProfile(
+      Profile* profile,
+      mojo::PendingReceiver<crosapi::mojom::PrintingMetricsForProfile> receiver,
+      mojo::PendingRemote<crosapi::mojom::PrintJobObserverForProfile> observer);
+
+ private:
+  mojo::UniqueReceiverSet<crosapi::mojom::PrintingMetricsForProfile> receivers_;
+};
+
+}  // namespace crosapi
+
+#endif  // CHROME_BROWSER_ASH_CROSAPI_PRINTING_METRICS_ASH_H_
diff --git a/chrome/browser/ash/login/encryption_migration_browsertest.cc b/chrome/browser/ash/login/encryption_migration_browsertest.cc
index c8a39a8..10270ca 100644
--- a/chrome/browser/ash/login/encryption_migration_browsertest.cc
+++ b/chrome/browser/ash/login/encryption_migration_browsertest.cc
@@ -80,8 +80,9 @@
   void SetUpOnMainThread() override {
     OobeBaseTest::SetUpOnMainThread();
 
-    FakeUserDataAuthClient::TestApi::Get()->SetEcryptfsUserHome(
-        GetTestCryptohomeId(), true);
+    FakeUserDataAuthClient::TestApi::Get()->SetHomeEncryptionMethod(
+        GetTestCryptohomeId(),
+        FakeUserDataAuthClient::HomeEncryptionMethod::kEcryptfs);
     FakeUserDataAuthClient::TestApi::Get()->set_run_default_dircrypto_migration(
         false);
 
diff --git a/chrome/browser/ash/login/screens/network_screen.cc b/chrome/browser/ash/login/screens/network_screen.cc
index 2a89796..c9a62fae 100644
--- a/chrome/browser/ash/login/screens/network_screen.cc
+++ b/chrome/browser/ash/login/screens/network_screen.cc
@@ -43,6 +43,8 @@
       return "Back";
     case Result::NOT_APPLICABLE:
     case Result::NOT_APPLICABLE_CONSOLIDATED_CONSENT:
+    case Result::NOT_APPLICABLE_CONNECTED_DEMO:
+    case Result::NOT_APPLICABLE_CONNECTED_DEMO_CONSOLIDATED_CONSENT:
       return BaseScreen::kNotApplicable;
   }
 }
@@ -261,9 +263,10 @@
     // Screen not shown yet: skipping it.
     if (DemoSetupController::IsOobeDemoSetupFlowInProgress()) {
       if (chromeos::features::IsOobeConsolidatedConsentEnabled())
-        exit_callback_.Run(Result::CONNECTED_DEMO_CONSOLIDATED_CONSENT);
+        exit_callback_.Run(
+            Result::NOT_APPLICABLE_CONNECTED_DEMO_CONSOLIDATED_CONSENT);
       else
-        exit_callback_.Run(Result::CONNECTED_DEMO);
+        exit_callback_.Run(Result::NOT_APPLICABLE_CONNECTED_DEMO);
     } else {
       if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
         exit_callback_.Run(Result::NOT_APPLICABLE_CONSOLIDATED_CONSENT);
diff --git a/chrome/browser/ash/login/screens/network_screen.h b/chrome/browser/ash/login/screens/network_screen.h
index 52bb73a5..5f6bcb5 100644
--- a/chrome/browser/ash/login/screens/network_screen.h
+++ b/chrome/browser/ash/login/screens/network_screen.h
@@ -37,7 +37,9 @@
     BACK_DEMO,
     BACK_OS_INSTALL,
     NOT_APPLICABLE,
-    NOT_APPLICABLE_CONSOLIDATED_CONSENT
+    NOT_APPLICABLE_CONSOLIDATED_CONSENT,
+    NOT_APPLICABLE_CONNECTED_DEMO,
+    NOT_APPLICABLE_CONNECTED_DEMO_CONSOLIDATED_CONSENT
   };
 
   static std::string GetResultString(Result result);
diff --git a/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc b/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
index d31722c..5a22db9a 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
@@ -87,9 +87,9 @@
 
   std::unique_ptr<base::HistogramTester> histogram_tester =
       std::make_unique<base::HistogramTester>();
-  FakeUserDataAuthClient::TestApi::Get()->SetEcryptfsUserHome(
+  FakeUserDataAuthClient::TestApi::Get()->SetHomeEncryptionMethod(
       cryptohome::CreateAccountIdentifierFromAccountId(users[1].account_id),
-      true);
+      FakeUserDataAuthClient::HomeEncryptionMethod::kEcryptfs);
 
   // Focus the 2nd user pod (consumer).
   ASSERT_TRUE(LoginScreenTestApi::FocusUser(users[1].account_id));
@@ -102,9 +102,9 @@
   histogram_tester->ExpectBucketCount("Ash.Login.Login.MigrationBanner", true,
                                       1);
 
-  FakeUserDataAuthClient::TestApi::Get()->SetEcryptfsUserHome(
+  FakeUserDataAuthClient::TestApi::Get()->SetHomeEncryptionMethod(
       cryptohome::CreateAccountIdentifierFromAccountId(users[2].account_id),
-      false);
+      FakeUserDataAuthClient::HomeEncryptionMethod::kDirCrypto);
   histogram_tester = std::make_unique<base::HistogramTester>();
   // Focus the 3rd user pod (consumer).
   ASSERT_TRUE(LoginScreenTestApi::FocusUser(users[2].account_id));
@@ -117,9 +117,9 @@
   histogram_tester->ExpectBucketCount("Ash.Login.Login.MigrationBanner", false,
                                       1);
 
-  FakeUserDataAuthClient::TestApi::Get()->SetEcryptfsUserHome(
+  FakeUserDataAuthClient::TestApi::Get()->SetHomeEncryptionMethod(
       cryptohome::CreateAccountIdentifierFromAccountId(users[3].account_id),
-      true);
+      FakeUserDataAuthClient::HomeEncryptionMethod::kEcryptfs);
   histogram_tester = std::make_unique<base::HistogramTester>();
 
   // Focus to the 4th user pod (enterprise).
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index af572cd4..0d9e1c3 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -1397,6 +1397,7 @@
       ShowEulaScreen();
       break;
     case NetworkScreen::Result::CONNECTED_DEMO:
+    case NetworkScreen::Result::NOT_APPLICABLE_CONNECTED_DEMO:
       DCHECK(demo_setup_controller_);
       demo_setup_controller_->set_demo_config(
           DemoSession::DemoModeConfig::kOnline);
@@ -1410,6 +1411,8 @@
       InitiateOOBEUpdate();
       break;
     case NetworkScreen::Result::CONNECTED_DEMO_CONSOLIDATED_CONSENT:
+    case NetworkScreen::Result::
+        NOT_APPLICABLE_CONNECTED_DEMO_CONSOLIDATED_CONSENT:
       DCHECK(demo_setup_controller_);
       demo_setup_controller_->set_demo_config(
           DemoSession::DemoModeConfig::kOnline);
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index b49f588..32ffc875 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -2216,7 +2216,7 @@
   ASSERT_EQ(1, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
 }
 
-class WizardControllerProxyAuthOnSigninTest : public WizardControllerTest {
+class WizardControllerProxyAuthOnSigninTest : public OobeBaseTest {
  public:
   WizardControllerProxyAuthOnSigninTest(
       const WizardControllerProxyAuthOnSigninTest&) = delete;
@@ -2232,17 +2232,11 @@
   // WizardControllerTest:
   void SetUp() override {
     ASSERT_TRUE(proxy_server_.Start());
-    WizardControllerTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    WizardControllerTest::SetUpOnMainThread();
-    WizardController::default_controller()->AdvanceToScreen(
-        WelcomeView::kScreenId);
+    OobeBaseTest::SetUp();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    WizardControllerTest::SetUpCommandLine(command_line);
+    OobeBaseTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII(::switches::kProxyServer,
                                     proxy_server_.host_port_pair().ToString());
   }
@@ -2250,19 +2244,20 @@
   net::SpawnedTestServer& proxy_server() { return proxy_server_; }
 
  private:
+  DeviceStateMixin device_state_{
+      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
   net::SpawnedTestServer proxy_server_;
 };
 
 // TODO(crbug.com/1286218): Flakes on CrOS.
 IN_PROC_BROWSER_TEST_F(WizardControllerProxyAuthOnSigninTest,
-                       DISABLED_ProxyAuthDialogOnSigninScreen) {
+                       ProxyAuthDialogOnSigninScreen) {
   content::WindowedNotificationObserver auth_needed_waiter(
       chrome::NOTIFICATION_AUTH_NEEDED,
       content::NotificationService::AllSources());
 
-  CheckCurrentScreen(WelcomeView::kScreenId);
+  OobeScreenWaiter(GaiaView::kScreenId).Wait();
 
-  LoginDisplayHost::default_host()->StartSignInScreen();
   auth_needed_waiter.Wait();
 }
 
diff --git a/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.cc b/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.cc
index c06a2e3..432247d 100644
--- a/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.cc
+++ b/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.cc
@@ -9,12 +9,11 @@
 #include "base/location.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h"
-#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/browser/notification_service.h"
 
 namespace policy {
 
@@ -36,13 +35,6 @@
                                 false);
 }
 
-void AppInstallEventLogManagerWrapper::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-}
-
 AppInstallEventLogManagerWrapper::AppInstallEventLogManagerWrapper(
     Profile* profile)
     : profile_(profile) {
@@ -54,8 +46,10 @@
       prefs::kArcAppInstallEventLoggingEnabled,
       base::BindRepeating(&AppInstallEventLogManagerWrapper::EvaluatePref,
                           base::Unretained(this)));
-  notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
-                              content::NotificationService::AllSources());
+  on_app_terminating_subscription_ =
+      browser_shutdown::AddAppTerminatingCallback(
+          base::BindOnce(&AppInstallEventLogManagerWrapper::OnAppTerminating,
+                         base::Unretained(this)));
 }
 
 void AppInstallEventLogManagerWrapper::Init() {
@@ -85,4 +79,8 @@
   }
 }
 
+void AppInstallEventLogManagerWrapper::OnAppTerminating() {
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.h b/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.h
index 65e0de3..58a7f56 100644
--- a/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.h
+++ b/chrome/browser/ash/policy/reporting/app_install_event_log_manager_wrapper.h
@@ -7,19 +7,13 @@
 
 #include <memory>
 
+#include "base/callback_list.h"
 #include "chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 
 class PrefRegistrySimple;
 class Profile;
 
-namespace content {
-class NotificationDetails;
-class NotificationSource;
-}  // namespace content
-
 namespace policy {
 
 // Observes the pref that indicates whether to log events for app push-installs.
@@ -28,14 +22,14 @@
 // data related to the app-install event log. Ensures correct sequencing of I/O
 // operations by using one |AppInstallEventLogManager::LogTaskRunnerWrapper| for
 // all accesses to the log file.
-class AppInstallEventLogManagerWrapper : public content::NotificationObserver {
+class AppInstallEventLogManagerWrapper {
  public:
   AppInstallEventLogManagerWrapper(const AppInstallEventLogManagerWrapper&) =
       delete;
   AppInstallEventLogManagerWrapper& operator=(
       const AppInstallEventLogManagerWrapper&) = delete;
 
-  ~AppInstallEventLogManagerWrapper() override;
+  virtual ~AppInstallEventLogManagerWrapper();
 
   // Creates a new |AppInstallEventLogManager| to handle app push-install event
   // logging for |profile|. The object returned manages its own lifetime and
@@ -44,11 +38,6 @@
 
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
  protected:
   explicit AppInstallEventLogManagerWrapper(Profile* profile);
 
@@ -73,6 +62,9 @@
   // clears all data related to the app-install event log.
   void EvaluatePref();
 
+  // Callback invoked when Chrome shuts down.
+  void OnAppTerminating();
+
   // The profile whose app push-install events are being logged.
   Profile* const profile_;
 
@@ -82,9 +74,9 @@
   // Pref change observer.
   PrefChangeRegistrar pref_change_registrar_;
 
-  // Notification observer, used to destroy the |log_manager_| when the user is
-  // logging out, giving it an opportunity to log the event.
-  content::NotificationRegistrar notification_registrar_;
+  // Browser shutdown callback, used to destroy the |log_manager_| when the
+  // user is logging out, giving it an opportunity to log the event.
+  base::CallbackListSubscription on_app_terminating_subscription_;
 };
 
 }  // namespace policy
diff --git a/chrome/browser/ash/printing/history/print_job_history_service.cc b/chrome/browser/ash/printing/history/print_job_history_service.cc
index 197d78c..16f7406 100644
--- a/chrome/browser/ash/printing/history/print_job_history_service.cc
+++ b/chrome/browser/ash/printing/history/print_job_history_service.cc
@@ -32,4 +32,10 @@
   observers_.RemoveObserver(observer);
 }
 
+void PrintJobHistoryService::Shutdown() {
+  for (auto& observer : observers_) {
+    observer.OnShutdown();
+  }
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/printing/history/print_job_history_service.h b/chrome/browser/ash/printing/history/print_job_history_service.h
index 56e9627b..6379db1c 100644
--- a/chrome/browser/ash/printing/history/print_job_history_service.h
+++ b/chrome/browser/ash/printing/history/print_job_history_service.h
@@ -20,6 +20,8 @@
    public:
     virtual void OnPrintJobFinished(
         const printing::proto::PrintJobInfo& print_job_info) = 0;
+
+    virtual void OnShutdown() {}
   };
 
   PrintJobHistoryService();
@@ -43,6 +45,9 @@
   void AddObserver(PrintJobHistoryService::Observer* observer);
   void RemoveObserver(PrintJobHistoryService::Observer* observer);
 
+  // KeyedService:
+  void Shutdown() override;
+
  protected:
   base::ObserverList<PrintJobHistoryService::Observer>::Unchecked observers_;
 };
diff --git a/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc
index f5c92ff..c62300d 100644
--- a/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/demo_mode_app_ui/demo_mode_app_ui.h"
+#include "ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h"
 #include "ash/webui/demo_mode_app_ui/url_constants.h"
 #include "ash/webui/web_applications/test/sandboxed_web_ui_test_base.h"
 #include "base/files/file_util.h"
@@ -39,10 +39,11 @@
     base::ScopedAllowBlockingForTesting allow_blocking;
     ASSERT_TRUE(component_dir_.CreateUniqueTempDir());
     content::WebUIConfigMap::GetInstance().RemoveForTesting(
-        url::Origin::Create(GURL(ash::kChromeUIDemoModeAppURL)));
-    content::WebUIConfigMap::GetInstance().AddWebUIConfig(
-        std::make_unique<ash::DemoModeAppUIConfig>(base::BindLambdaForTesting(
-            [&] { return component_dir_.GetPath(); })));
+        url::Origin::Create(GURL(ash::kChromeUntrustedUIDemoModeAppURL)));
+    content::WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
+        std::make_unique<ash::DemoModeAppUntrustedUIConfig>(
+            base::BindLambdaForTesting(
+                [&] { return component_dir_.GetPath(); })));
   }
 
   base::ScopedTempDir component_dir_;
@@ -83,7 +84,7 @@
 
 // Test that the Demo Mode App installs and launches correctly
 IN_PROC_BROWSER_TEST_P(DemoModeAppIntegrationTest, DemoModeApp) {
-  const GURL url(ash::kChromeUIDemoModeAppURL);
+  const GURL url(ash::kChromeUntrustedUIDemoModeAppURL);
   EXPECT_NO_FATAL_FAILURE(ExpectSystemWebAppValid(
       ash::SystemWebAppType::DEMO_MODE, url, "Demo Mode App"));
 }
@@ -116,8 +117,8 @@
 
   apps::AppLaunchParams params =
       LaunchParamsForApp(ash::SystemWebAppType::DEMO_MODE);
-  params.override_url =
-      GURL("chrome://demo-mode-app/" + file_path.BaseName().MaybeAsASCII());
+  params.override_url = GURL(ash::kChromeUntrustedUIDemoModeAppURL +
+                             file_path.BaseName().MaybeAsASCII());
   content::WebContents* web_contents = LaunchApp(std::move(params));
 
   EXPECT_EQ(
@@ -134,7 +135,8 @@
 
   apps::AppLaunchParams params =
       LaunchParamsForApp(ash::SystemWebAppType::DEMO_MODE);
-  params.override_url = GURL("chrome://demo-mode-app/nonexistent.html");
+  params.override_url =
+      GURL("chrome-untrusted://demo-mode-app/nonexistent.html");
   content::WebContents* web_contents = LaunchApp(std::move(params));
 
   EXPECT_EQ(
diff --git a/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc b/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
index f6405b7..1014005 100644
--- a/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
@@ -15,8 +15,8 @@
 std::unique_ptr<WebAppInstallInfo> CreateWebAppInfoForDemoModeApp() {
   std::unique_ptr<WebAppInstallInfo> info =
       std::make_unique<WebAppInstallInfo>();
-  info->start_url = GURL(ash::kChromeUIDemoModeAppURL);
-  info->scope = GURL(ash::kChromeUIDemoModeAppURL);
+  info->start_url = GURL(ash::kChromeUntrustedUIDemoModeAppURL);
+  info->scope = GURL(ash::kChromeUntrustedUIDemoModeAppURL);
   // TODO(b/185608502): Convert the title to a localized string
   info->title = u"Demo Mode App";
   web_app::CreateIconInfoForSystemWebApp(
@@ -34,7 +34,7 @@
 DemoModeSystemAppDelegate::DemoModeSystemAppDelegate(Profile* profile)
     : ash::SystemWebAppDelegate(ash::SystemWebAppType::DEMO_MODE,
                                 "DemoMode",
-                                GURL("chrome://demo-mode-app"),
+                                GURL(ash::kChromeUntrustedUIDemoModeAppURL),
                                 profile) {}
 
 std::unique_ptr<WebAppInstallInfo> DemoModeSystemAppDelegate::GetWebAppInfo()
diff --git a/chrome/browser/browsing_data/access_context_audit_browsertest.cc b/chrome/browser/browsing_data/access_context_audit_browsertest.cc
index 765611a..4a7552b 100644
--- a/chrome/browser/browsing_data/access_context_audit_browsertest.cc
+++ b/chrome/browser/browsing_data/access_context_audit_browsertest.cc
@@ -474,8 +474,8 @@
   EXPECT_EQ(cookies.size(),
             kEmbeddedPageCookieCount + kTopLevelPageCookieCount);
 
-  auto tree_model =
-      CookiesTreeModel::CreateForProfile(chrome_test_utils::GetProfile(this));
+  auto tree_model = CookiesTreeModel::CreateForProfileDeprecated(
+      chrome_test_utils::GetProfile(this));
   {
     CookiesTreeObserver observer;
     tree_model->AddCookiesTreeObserver(&observer);
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper.h b/chrome/browser/browsing_data/browsing_data_quota_helper.h
index 631404f..69e994f 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper.h
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/task/sequenced_task_runner_helpers.h"
+#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 
 class BrowsingDataQuotaHelper;
 class Profile;
@@ -69,6 +70,9 @@
 
   virtual void RevokeHostQuota(const std::string& host) = 0;
 
+  virtual void DeleteHostData(const std::string& host,
+                              blink::mojom::StorageType type) = 0;
+
  protected:
   BrowsingDataQuotaHelper();
   virtual ~BrowsingDataQuotaHelper();
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
index 6aae854..4a93ad8 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
@@ -52,6 +52,15 @@
                      this, host));
 }
 
+void BrowsingDataQuotaHelperImpl::DeleteHostData(const std::string& host,
+                                                 StorageType type) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  content::GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&BrowsingDataQuotaHelperImpl::DeleteHostDataOnIOThread,
+                     this, host, type));
+}
+
 BrowsingDataQuotaHelperImpl::BrowsingDataQuotaHelperImpl(
     storage::QuotaManager* quota_manager)
     : BrowsingDataQuotaHelper(), quota_manager_(quota_manager) {
@@ -162,6 +171,13 @@
                      weak_factory_.GetWeakPtr()));
 }
 
+void BrowsingDataQuotaHelperImpl::DeleteHostDataOnIOThread(
+    const std::string& host,
+    blink::mojom::StorageType type) {
+  quota_manager_->DeleteHostData(
+      host, type, base::DoNothingAs<void(blink::mojom::QuotaStatusCode)>());
+}
+
 void BrowsingDataQuotaHelperImpl::DidRevokeHostQuota(
     blink::mojom::QuotaStatusCode /*status*/,
     int64_t /*quota*/) {}
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
index 55f4189..9080c09 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
@@ -34,6 +34,8 @@
  public:
   void StartFetching(FetchResultCallback callback) override;
   void RevokeHostQuota(const std::string& host) override;
+  void DeleteHostData(const std::string& host,
+                      blink::mojom::StorageType type) override;
 
   explicit BrowsingDataQuotaHelperImpl(storage::QuotaManager* quota_manager);
 
@@ -71,6 +73,9 @@
   void RevokeHostQuotaOnIOThread(const std::string& host);
   void DidRevokeHostQuota(blink::mojom::QuotaStatusCode status, int64_t quota);
 
+  void DeleteHostDataOnIOThread(const std::string& host,
+                                blink::mojom::StorageType type);
+
   scoped_refptr<storage::QuotaManager> quota_manager_;
 
   base::WeakPtrFactory<BrowsingDataQuotaHelperImpl> weak_factory_{this};
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
index f4f963a..d770cbf 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
@@ -147,6 +147,10 @@
     helper_->RevokeHostQuota(host);
   }
 
+  void DeleteHostData(const std::string& host, blink::mojom::StorageType type) {
+    helper_->DeleteHostData(host, type);
+  }
+
   int64_t quota() { return quota_; }
 
  private:
@@ -245,3 +249,38 @@
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(10, quota());
 }
+
+TEST_F(BrowsingDataQuotaHelperTest, DeleteHostData) {
+  static const ClientDefaultBucketData kStorageKeys[] = {
+      {"http://example.com/", StorageType::kTemporary, 1},
+      {"https://example.com/", StorageType::kTemporary, 10},
+      {"http://example.com/", StorageType::kPersistent, 100},
+      {"https://example.com/", StorageType::kSyncable, 1},
+      {"http://example2.com/", StorageType::kTemporary, 1000},
+  };
+  RegisterClient(kStorageKeys);
+
+  DeleteHostData("example.com", StorageType::kPersistent);
+
+  StartFetching();
+  content::RunAllTasksUntilIdle();
+  EXPECT_TRUE(fetching_completed());
+
+  std::set<QuotaInfo> expected, actual;
+  actual.insert(quota_info().begin(), quota_info().end());
+  expected.insert(QuotaInfo("example.com", 11, 0, 1));
+  expected.insert(QuotaInfo("example2.com", 1000, 0, 0));
+  EXPECT_TRUE(expected == actual);
+
+  DeleteHostData("example2.com", StorageType::kTemporary);
+
+  StartFetching();
+  content::RunAllTasksUntilIdle();
+  EXPECT_TRUE(fetching_completed());
+
+  expected.clear();
+  actual.clear();
+  actual.insert(quota_info().begin(), quota_info().end());
+  expected.insert(QuotaInfo("example.com", 11, 0, 1));
+  EXPECT_TRUE(expected == actual);
+}
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index 27d85b0..41cdc4f 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -51,6 +51,7 @@
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
@@ -635,12 +636,21 @@
   ~CookieTreeQuotaNode() override = default;
 
   void DeleteStoredObjects() override {
-    // Calling this function may cause unexpected over-quota state of origin.
-    // However, it'll caused no problem, just prevent usage growth of the
-    // origin.
     LocalDataContainer* container = GetModel()->data_container();
 
     if (container) {
+      if (quota_info_->temporary_usage > 0) {
+        container->quota_helper_->DeleteHostData(
+            quota_info_->host, blink::mojom::StorageType::kTemporary);
+      }
+      if (quota_info_->persistent_usage > 0) {
+        container->quota_helper_->DeleteHostData(
+            quota_info_->host, blink::mojom::StorageType::kPersistent);
+      }
+      if (quota_info_->syncable_usage > 0) {
+        container->quota_helper_->DeleteHostData(
+            quota_info_->host, blink::mojom::StorageType::kSyncable);
+      }
       container->quota_helper_->RevokeHostQuota(quota_info_->host);
       container->quota_info_list_.erase(quota_info_);
     }
@@ -1876,30 +1886,57 @@
 }
 
 // static
-std::unique_ptr<CookiesTreeModel> CookiesTreeModel::CreateForProfile(
+std::unique_ptr<CookiesTreeModel> CookiesTreeModel::CreateForProfileDeprecated(
     Profile* profile) {
   auto* storage_partition = profile->GetDefaultStoragePartition();
   auto* file_system_context = storage_partition->GetFileSystemContext();
   auto* native_io_context = storage_partition->GetNativeIOContext();
 
+  // If partitioned storage is enabled, the quota node is used to represent all
+  // types of quota managed storage. If not, the quota node type is excluded as
+  // it is represented by other types.
+  bool use_quota_only = base::FeatureList::IsEnabled(
+      blink::features::kThirdPartyStoragePartitioning);
+
+  // Types managed by Quota:
+  auto database_helper =
+      use_quota_only
+          ? nullptr
+          : base::MakeRefCounted<browsing_data::DatabaseHelper>(profile);
+  auto cache_helper =
+      use_quota_only ? nullptr
+                     : base::MakeRefCounted<browsing_data::CacheStorageHelper>(
+                           storage_partition);
+  auto indexed_db_helper =
+      use_quota_only ? nullptr
+                     : base::MakeRefCounted<browsing_data::IndexedDBHelper>(
+                           storage_partition);
+  auto file_system_helper =
+      use_quota_only
+          ? nullptr
+          : base::MakeRefCounted<browsing_data::FileSystemHelper>(
+                file_system_context,
+                browsing_data_file_system_util::GetAdditionalFileSystemTypes(),
+                native_io_context);
+  auto service_worker_helper =
+      use_quota_only ? nullptr
+                     : base::MakeRefCounted<browsing_data::ServiceWorkerHelper>(
+                           storage_partition->GetServiceWorkerContext());
+
+  // Quota type itself:
+  auto quota_helper =
+      use_quota_only ? BrowsingDataQuotaHelper::Create(profile) : nullptr;
+
   auto container = std::make_unique<LocalDataContainer>(
       base::MakeRefCounted<browsing_data::CookieHelper>(
           storage_partition, GetCookieDeletionDisabledCallback(profile)),
-      base::MakeRefCounted<browsing_data::DatabaseHelper>(profile),
+      database_helper,
       base::MakeRefCounted<browsing_data::LocalStorageHelper>(profile),
-      /*session_storage_helper=*/nullptr,
-      base::MakeRefCounted<browsing_data::IndexedDBHelper>(storage_partition),
-      base::MakeRefCounted<browsing_data::FileSystemHelper>(
-          file_system_context,
-          browsing_data_file_system_util::GetAdditionalFileSystemTypes(),
-          native_io_context),
-      BrowsingDataQuotaHelper::Create(profile),
-      base::MakeRefCounted<browsing_data::ServiceWorkerHelper>(
-          storage_partition->GetServiceWorkerContext()),
+      /*session_storage_helper=*/nullptr, indexed_db_helper, file_system_helper,
+      quota_helper, service_worker_helper,
       base::MakeRefCounted<browsing_data::SharedWorkerHelper>(
           storage_partition),
-      base::MakeRefCounted<browsing_data::CacheStorageHelper>(
-          storage_partition),
+      cache_helper,
       BrowsingDataMediaLicenseHelper::Create(file_system_context));
 
   return std::make_unique<CookiesTreeModel>(
diff --git a/chrome/browser/browsing_data/cookies_tree_model.h b/chrome/browser/browsing_data/cookies_tree_model.h
index 928f998..f96364a 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.h
+++ b/chrome/browser/browsing_data/cookies_tree_model.h
@@ -380,7 +380,11 @@
   void SetBatchExpectation(int batches_expected, bool reset);
 
   // Create CookiesTreeModel by profile info.
-  static std::unique_ptr<CookiesTreeModel> CreateForProfile(Profile* profile);
+  // DEPRECATED(crbug.com/1271155): The cookies tree model is slowly being
+  // deprecated, during this process the semantics of the model are nuanced
+  // w.r.t partitioned storage, and should not be used in new locations.
+  static std::unique_ptr<CookiesTreeModel> CreateForProfileDeprecated(
+      Profile* profile);
 
   static browsing_data::CookieHelper::IsDeletionDisabledCallback
   GetCookieDeletionDisabledCallback(Profile* profile);
diff --git a/chrome/browser/browsing_data/cookies_tree_model_browsertest.cc b/chrome/browser/browsing_data/cookies_tree_model_browsertest.cc
new file mode 100644
index 0000000..cfeb8bb
--- /dev/null
+++ b/chrome/browser/browsing_data/cookies_tree_model_browsertest.cc
@@ -0,0 +1,188 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/browsing_data/cookies_tree_model.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace {
+
+// Calls the accessStorage javascript function and awaits its completion for
+// each frame in the active web contents for |browser|.
+void EnsurePageAccessedStorage(content::WebContents* web_contents) {
+  web_contents->GetPrimaryMainFrame()->ForEachRenderFrameHost(
+      base::BindRepeating([](content::RenderFrameHost* frame) {
+        EXPECT_TRUE(
+            content::EvalJs(frame,
+                            "(async () => { return await accessStorage();})()")
+                .value.GetBool());
+      }));
+}
+
+std::vector<CookieTreeNode*> GetAllChildNodes(CookieTreeNode* node) {
+  std::vector<CookieTreeNode*> nodes;
+  for (const auto& child : node->children()) {
+    auto child_nodes = GetAllChildNodes(child.get());
+    nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end());
+  }
+  nodes.push_back(node);
+  return nodes;
+}
+
+std::map<CookieTreeNode::DetailedInfo::NodeType, int> GetNodeTypeCounts(
+    CookiesTreeModel* model) {
+  auto nodes = GetAllChildNodes(model->GetRoot());
+
+  std::map<CookieTreeNode::DetailedInfo::NodeType, int> node_counts_map;
+  for (auto* node : nodes)
+    node_counts_map[node->GetDetailedInfo().node_type]++;
+
+  return node_counts_map;
+}
+
+}  // namespace
+
+class CookiesTreeObserver : public CookiesTreeModel::Observer {
+ public:
+  void AwaitTreeModelEndBatch() {
+    run_loop = std::make_unique<base::RunLoop>();
+    run_loop->Run();
+  }
+
+  void TreeModelEndBatch(CookiesTreeModel* model) override { run_loop->Quit(); }
+
+  void TreeNodesAdded(ui::TreeModel* model,
+                      ui::TreeModelNode* parent,
+                      size_t start,
+                      size_t count) override {}
+  void TreeNodesRemoved(ui::TreeModel* model,
+                        ui::TreeModelNode* parent,
+                        size_t start,
+                        size_t count) override {}
+  void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
+  }
+
+ private:
+  std::unique_ptr<base::RunLoop> run_loop;
+};
+
+// TODO(crbug.com/1271155): This test copies logic from the Access Context Audit
+// Service test. At least this test, and likely the ACA service & test, can be
+// removed when the tree model is deprecated.
+class CookiesTreeModelBrowserTest : public InProcessBrowserTest {
+ public:
+  void SetUp() override {
+    InitFeatures();
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    test_server_.ServeFilesFromSourceDirectory(
+        base::FilePath(FILE_PATH_LITERAL("content/test/data")));
+    test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+    ASSERT_TRUE(test_server_.Start());
+  }
+
+  void AccessStorage() {
+    ASSERT_TRUE(content::NavigateToURL(
+        chrome_test_utils::GetActiveWebContents(this), storage_accessor_url()));
+    base::RunLoop().RunUntilIdle();
+    EnsurePageAccessedStorage(chrome_test_utils::GetActiveWebContents(this));
+  }
+
+  GURL storage_accessor_url() {
+    auto host_port_pair =
+        net::HostPortPair::FromURL(test_server_.GetURL("a.test", "/"));
+    base::StringPairs replacement_text = {
+        {"REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString()}};
+    auto replaced_path = net::test_server::GetFilePathWithReplacements(
+        "/browsing_data/storage_accessor.html", replacement_text);
+    return test_server_.GetURL("a.test", replaced_path);
+  }
+
+  virtual void InitFeatures() {
+    feature_list()->InitAndDisableFeature(
+        blink::features::kThirdPartyStoragePartitioning);
+  }
+
+  base::test::ScopedFeatureList* feature_list() { return &feature_list_; }
+
+ protected:
+  net::EmbeddedTestServer test_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(CookiesTreeModelBrowserTest, NoQuotaStorage) {
+  AccessStorage();
+
+  auto tree_model = CookiesTreeModel::CreateForProfileDeprecated(
+      chrome_test_utils::GetProfile(this));
+  CookiesTreeObserver observer;
+  tree_model->AddCookiesTreeObserver(&observer);
+  observer.AwaitTreeModelEndBatch();
+
+  // Quota storage has been accessed, but should not be present in the tree.
+  EXPECT_EQ(17, tree_model->GetRoot()->GetTotalNodeCount());
+  auto node_counts = GetNodeTypeCounts(tree_model.get());
+  EXPECT_EQ(16u, node_counts.size());
+  EXPECT_EQ(0, node_counts[CookieTreeNode::DetailedInfo::TYPE_QUOTA]);
+
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_ROOT]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_HOST]);
+  EXPECT_EQ(2, node_counts[CookieTreeNode::DetailedInfo::TYPE_COOKIE]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_COOKIES]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_DATABASE]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_DATABASES]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGES]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_INDEXED_DBS]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEMS]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_SERVICE_WORKER]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_SERVICE_WORKERS]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_CACHE_STORAGE]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_CACHE_STORAGES]);
+}
+
+class CookiesTreeModelBrowserTestQuotaOnly
+    : public CookiesTreeModelBrowserTest {
+ public:
+  void InitFeatures() override {
+    feature_list()->InitAndEnableFeature(
+        blink::features::kThirdPartyStoragePartitioning);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(CookiesTreeModelBrowserTestQuotaOnly, QuotaStorageOnly) {
+  AccessStorage();
+
+  auto tree_model = CookiesTreeModel::CreateForProfileDeprecated(
+      chrome_test_utils::GetProfile(this));
+  CookiesTreeObserver observer;
+  tree_model->AddCookiesTreeObserver(&observer);
+  observer.AwaitTreeModelEndBatch();
+
+  // Quota storage has been accessed, only quota nodes should be present for
+  // quota managed storage types.
+  EXPECT_EQ(8, tree_model->GetRoot()->GetTotalNodeCount());
+
+  auto node_counts = GetNodeTypeCounts(tree_model.get());
+  EXPECT_EQ(7u, node_counts.size());
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_ROOT]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_HOST]);
+  EXPECT_EQ(2, node_counts[CookieTreeNode::DetailedInfo::TYPE_COOKIE]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_COOKIES]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGES]);
+  EXPECT_EQ(1, node_counts[CookieTreeNode::DetailedInfo::TYPE_QUOTA]);
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc
index 9e94731..1e70dd80 100644
--- a/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc
+++ b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc
@@ -23,6 +23,10 @@
 void MockBrowsingDataQuotaHelper::RevokeHostQuota(const std::string& host) {
 }
 
+void MockBrowsingDataQuotaHelper::DeleteHostData(
+    const std::string& host,
+    blink::mojom::StorageType type) {}
+
 void MockBrowsingDataQuotaHelper::AddHost(const std::string& host,
                                           int64_t temporary_usage,
                                           int64_t persistent_usage,
diff --git a/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h
index b937ad5..dd49c1ad 100644
--- a/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h
+++ b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h
@@ -22,6 +22,8 @@
 
   void StartFetching(FetchResultCallback callback) override;
   void RevokeHostQuota(const std::string& host) override;
+  void DeleteHostData(const std::string& host,
+                      blink::mojom::StorageType type) override;
 
   void AddHost(const std::string& host,
                int64_t temporary_usage,
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 3807493..57d8ee6 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -291,8 +291,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
-#include "ash/webui/demo_mode_app_ui/demo_mode_app_ui.h"
-#include "ash/webui/demo_mode_app_ui/mojom/demo_mode_app_ui.mojom.h"
+#include "ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h"
 #include "ash/webui/sample_system_web_app_ui/mojom/sample_system_web_app_ui.mojom.h"
 #include "ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.h"
 #include "ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.h"
@@ -1167,13 +1166,6 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
-  if (ash::features::IsDemoModeSWAEnabled()) {
-    RegisterWebUIControllerInterfaceBinder<
-        ash::mojom::demo_mode::PageHandlerFactory, ash::DemoModeAppUI>(map);
-  }
-#endif
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS)
   RegisterWebUIControllerInterfaceBinder<discards::mojom::DetailsProvider,
@@ -1248,6 +1240,8 @@
   // --- Section 2: chrome-untrusted:// WebUIs:
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
+  registry.ForWebUI<ash::DemoModeAppUntrustedUI>()
+      .Add<ash::mojom::demo_mode::UntrustedPageHandlerFactory>();
   registry.ForWebUI<ash::UntrustedSampleSystemWebAppUI>()
       .Add<ash::mojom::sample_swa::UntrustedPageInterfacesFactory>();
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index fea2c72..8e12b8a2 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1876,12 +1876,10 @@
       "../ash/printing/cups_proxy_service_manager_factory.cc",
       "../ash/printing/cups_proxy_service_manager_factory.h",
       "../ash/printing/printer_info_cups.cc",
-      "extensions/printing_metrics/print_job_finished_event_dispatcher.cc",
-      "extensions/printing_metrics/print_job_finished_event_dispatcher.h",
-      "extensions/printing_metrics/print_job_info_idl_conversions.cc",
-      "extensions/printing_metrics/print_job_info_idl_conversions.h",
       "extensions/printing_metrics/printing_metrics_api.cc",
       "extensions/printing_metrics/printing_metrics_api.h",
+      "extensions/printing_metrics/printing_metrics_service.cc",
+      "extensions/printing_metrics/printing_metrics_service.h",
     ]
   } else {
     sources += [
@@ -3103,8 +3101,6 @@
   if (use_cups) {
     sources += [
       "../ash/printing/cups_print_job_manager_utils_unittest.cc",
-      "extensions/printing_metrics/print_job_finished_event_dispatcher_unittest.cc",
-      "extensions/printing_metrics/print_job_info_idl_conversions_unittest.cc",
       "extensions/printing_metrics/printing_metrics_api_unittest.cc",
     ]
   }
diff --git a/chrome/browser/chromeos/extensions/login_screen/OWNERS b/chrome/browser/chromeos/extensions/login_screen/OWNERS
index aef9c2c4..24cfb61 100644
--- a/chrome/browser/chromeos/extensions/login_screen/OWNERS
+++ b/chrome/browser/chromeos/extensions/login_screen/OWNERS
@@ -1 +1,2 @@
 hendrich@chromium.org
+mpetrisor@chromium.org
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/OWNERS b/chrome/browser/chromeos/extensions/printing_metrics/OWNERS
index cd76a71..9dcc4431 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/OWNERS
+++ b/chrome/browser/chromeos/extensions/printing_metrics/OWNERS
@@ -1,3 +1,4 @@
 srad@google.com
 pmarko@chromium.org
 pawliczek@chromium.org
+greengrape@google.com
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc
deleted file mode 100644
index e9bafa2..0000000
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.h"
-
-#include "base/no_destructor.h"
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h"
-#include "chrome/browser/extensions/api/printing/printing_api.h"
-#include "chrome/common/extensions/api/printing_metrics.h"
-#include "content/public/browser/browser_context.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_event_histogram_value.h"
-
-namespace extensions {
-
-BrowserContextKeyedAPIFactory<PrintJobFinishedEventDispatcher>*
-PrintJobFinishedEventDispatcher::GetFactoryInstance() {
-  static base::NoDestructor<
-      BrowserContextKeyedAPIFactory<PrintJobFinishedEventDispatcher>>
-      instance;
-  return instance.get();
-}
-
-PrintJobFinishedEventDispatcher::PrintJobFinishedEventDispatcher(
-    content::BrowserContext* browser_context)
-    : browser_context_(browser_context),
-      event_router_(EventRouter::Get(browser_context)) {
-  auto* history_service =
-      ash::PrintJobHistoryServiceFactory::GetForBrowserContext(browser_context);
-
-  // The print job history service is not available on the lock screen.
-  if (history_service) {
-    print_job_history_service_observation_.Observe(history_service);
-  }
-}
-
-PrintJobFinishedEventDispatcher::~PrintJobFinishedEventDispatcher() {}
-
-void PrintJobFinishedEventDispatcher::OnPrintJobFinished(
-    const ash::printing::proto::PrintJobInfo& print_job_info) {
-  auto event = std::make_unique<Event>(
-      events::PRINTING_METRICS_ON_PRINT_JOB_FINISHED,
-      api::printing_metrics::OnPrintJobFinished::kEventName,
-      api::printing_metrics::OnPrintJobFinished::Create(
-          PrintJobInfoProtoToIdl(print_job_info)));
-
-  event_router_->BroadcastEvent(std::move(event));
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.h b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.h
deleted file mode 100644
index a2e62c57..0000000
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINT_JOB_FINISHED_EVENT_DISPATCHER_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINT_JOB_FINISHED_EVENT_DISPATCHER_H_
-
-#include "base/scoped_observation.h"
-#include "chrome/browser/ash/printing/history/print_job_history_service.h"
-#include "chrome/browser/ash/printing/history/print_job_history_service_factory.h"
-#include "extensions/browser/browser_context_keyed_api_factory.h"
-#include "extensions/browser/event_router_factory.h"
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace extensions {
-
-class EventRouter;
-
-// PrintJobFinishedEventDispatcher observes changes in the print job history
-// service and dispatches them to extensions listening on the
-// printingMetrics.onPrintJobFinished() event.
-class PrintJobFinishedEventDispatcher
-    : public BrowserContextKeyedAPI,
-      public ash::PrintJobHistoryService::Observer {
- public:
-  explicit PrintJobFinishedEventDispatcher(
-      content::BrowserContext* browser_context);
-
-  PrintJobFinishedEventDispatcher(const PrintJobFinishedEventDispatcher&) =
-      delete;
-  PrintJobFinishedEventDispatcher& operator=(
-      const PrintJobFinishedEventDispatcher&) = delete;
-
-  ~PrintJobFinishedEventDispatcher() override;
-
-  // BrowserContextKeyedAPI:
-  static BrowserContextKeyedAPIFactory<PrintJobFinishedEventDispatcher>*
-  GetFactoryInstance();
-
-  // PrintJobHistoryService::Observer:
-  void OnPrintJobFinished(
-      const ash::printing::proto::PrintJobInfo& print_job_info) override;
-
- private:
-  // Needed for BrowserContextKeyedAPI implementation.
-  friend class BrowserContextKeyedAPIFactory<PrintJobFinishedEventDispatcher>;
-
-  // BrowserContextKeyedAPI implementation.
-  static const bool kServiceIsNULLWhileTesting = true;
-  static const char* service_name() {
-    return "PrintJobFinishedEventDispatcher";
-  }
-
-  content::BrowserContext* browser_context_;
-  EventRouter* event_router_;
-  base::ScopedObservation<ash::PrintJobHistoryService,
-                          ash::PrintJobHistoryService::Observer>
-      print_job_history_service_observation_{this};
-};
-
-template <>
-struct BrowserContextFactoryDependencies<PrintJobFinishedEventDispatcher> {
-  static void DeclareFactoryDependencies(
-      BrowserContextKeyedAPIFactory<PrintJobFinishedEventDispatcher>* factory) {
-    factory->DependsOn(EventRouterFactory::GetInstance());
-    factory->DependsOn(ash::PrintJobHistoryServiceFactory::GetInstance());
-  }
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINT_JOB_FINISHED_EVENT_DISPATCHER_H_
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_unittest.cc b/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_unittest.cc
deleted file mode 100644
index 095d9e9..0000000
--- a/chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher_unittest.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_finished_event_dispatcher.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/values.h"
-#include "chrome/browser/ash/printing/history/mock_print_job_history_service.h"
-#include "chrome/browser/ash/printing/history/print_job_history_service_factory.h"
-#include "chrome/browser/ash/printing/history/print_job_info.pb.h"
-#include "chrome/browser/ash/printing/history/test_print_job_history_service_observer.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/extensions/api/printing_metrics.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "content/public/test/browser_task_environment.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/event_router_factory.h"
-#include "extensions/browser/test_event_router_observer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-std::unique_ptr<extensions::api::printing_metrics::PrintJobInfo>
-GetPrintJobFinishedEventValue(
-    const extensions::TestEventRouterObserver* observer) {
-  const auto& event_map = observer->events();
-  auto iter = event_map.find(
-      extensions::api::printing_metrics::OnPrintJobFinished::kEventName);
-  if (iter == event_map.end())
-    return nullptr;
-
-  const extensions::Event& event = *iter->second;
-  if (event.event_args.size() != 1u) {
-    ADD_FAILURE() << "Invalid event args";
-    return nullptr;
-  }
-
-  return extensions::api::printing_metrics::PrintJobInfo::FromValue(
-      event.event_args[0]);
-}
-
-// Creates a new MockPrintJobHistoryService for the given |context|.
-std::unique_ptr<KeyedService> BuildPrintJobHistoryService(
-    content::BrowserContext* context) {
-  return std::make_unique<ash::MockPrintJobHistoryService>();
-}
-
-// Creates a new EventRouter for the given |context|.
-std::unique_ptr<KeyedService> BuildEventRouter(
-    content::BrowserContext* context) {
-  return std::make_unique<extensions::EventRouter>(context, nullptr);
-}
-
-}  // namespace
-
-namespace extensions {
-
-class PrintJobFinishedEventDispatcherUnittest : public testing::Test {
- public:
-  PrintJobFinishedEventDispatcherUnittest() {}
-
-  PrintJobFinishedEventDispatcherUnittest(
-      const PrintJobFinishedEventDispatcherUnittest&) = delete;
-  PrintJobFinishedEventDispatcherUnittest& operator=(
-      const PrintJobFinishedEventDispatcherUnittest&) = delete;
-
-  ~PrintJobFinishedEventDispatcherUnittest() override = default;
-
-  void SetUp() override {
-    profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(profile_manager_->SetUp());
-    testing_profile_ =
-        profile_manager_->CreateTestingProfile(chrome::kInitialProfile);
-
-    ash::PrintJobHistoryServiceFactory::GetInstance()->SetTestingFactory(
-        testing_profile_, base::BindRepeating(&BuildPrintJobHistoryService));
-
-    EventRouterFactory::GetInstance()->SetTestingFactory(
-        testing_profile_, base::BindRepeating(&BuildEventRouter));
-
-    dispatcher_ =
-        std::make_unique<PrintJobFinishedEventDispatcher>(testing_profile_);
-    observer_ = std::make_unique<TestEventRouterObserver>(
-        EventRouter::Get(testing_profile_));
-  }
-
-  void TearDown() override {
-    observer_.reset();
-    dispatcher_.reset();
-
-    testing_profile_ = nullptr;
-    profile_manager_->DeleteTestingProfile(chrome::kInitialProfile);
-  }
-
- protected:
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile* testing_profile_;
-  std::unique_ptr<TestEventRouterObserver> observer_;
-
- private:
-  std::unique_ptr<TestingProfileManager> profile_manager_;
-  std::unique_ptr<PrintJobFinishedEventDispatcher> dispatcher_;
-};
-
-// Test that |OnPrintJobFinished| is dispatched when the print job is saved by
-// PrintJobHistoryService.
-TEST_F(PrintJobFinishedEventDispatcherUnittest, EventIsDispatched) {
-  constexpr char kTitle[] = "title";
-  constexpr int kPagesNumber = 3;
-
-  base::RunLoop run_loop;
-  ash::MockPrintJobHistoryService* print_job_history_service =
-      static_cast<ash::MockPrintJobHistoryService*>(
-          ash::PrintJobHistoryServiceFactory::GetForBrowserContext(
-              testing_profile_));
-  ash::TestPrintJobHistoryServiceObserver observer(
-      print_job_history_service, run_loop.QuitWhenIdleClosure());
-
-  ash::printing::proto::PrintJobInfo print_job_info_proto;
-  print_job_info_proto.set_title(kTitle);
-  print_job_info_proto.set_status(
-      ash::printing::proto::PrintJobInfo_PrintJobStatus_FAILED);
-  print_job_info_proto.set_number_of_pages(kPagesNumber);
-  print_job_history_service->SavePrintJobProto(print_job_info_proto);
-  run_loop.Run();
-
-  // As soon as Run() is completed all PrintJobHistoryService observers are
-  // called and event is expected to appear in the |observer_|.
-  std::unique_ptr<extensions::api::printing_metrics::PrintJobInfo>
-      print_job_info = GetPrintJobFinishedEventValue(observer_.get());
-  ASSERT_TRUE(print_job_info);
-  EXPECT_EQ(kTitle, print_job_info->title);
-  EXPECT_EQ(extensions::api::printing_metrics::PRINT_JOB_STATUS_FAILED,
-            print_job_info->status);
-  EXPECT_EQ(kPagesNumber, print_job_info->number_of_pages);
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.cc b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.cc
index 55df05e2..1e81191 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.cc
+++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.cc
@@ -4,9 +4,7 @@
 
 #include "chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.h"
 
-#include "chrome/browser/ash/printing/history/print_job_history_service.h"
-#include "chrome/browser/ash/printing/history/print_job_history_service_factory.h"
-#include "chrome/browser/chromeos/extensions/printing_metrics/print_job_info_idl_conversions.h"
+#include "chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.h"
 #include "chrome/common/extensions/api/printing_metrics.h"
 #include "content/public/browser/browser_context.h"
 
@@ -16,23 +14,21 @@
     default;
 
 ExtensionFunction::ResponseAction PrintingMetricsGetPrintJobsFunction::Run() {
-  ash::PrintJobHistoryService* print_job_history_service =
-      ash::PrintJobHistoryServiceFactory::GetForBrowserContext(
-          browser_context());
-  print_job_history_service->GetPrintJobs(base::BindOnce(
-      &PrintingMetricsGetPrintJobsFunction::OnPrintJobsRetrieved, this));
+  PrintingMetricsService::Get(browser_context())
+      ->GetPrintJobs(base::BindOnce(
+          &PrintingMetricsGetPrintJobsFunction::OnPrintJobsRetrieved, this));
 
-  // GetPrintJobs might have already responded.
-  return did_respond() ? AlreadyResponded() : RespondLater();
+  return RespondLater();
 }
 
 void PrintingMetricsGetPrintJobsFunction::OnPrintJobsRetrieved(
-    bool success,
-    std::vector<ash::printing::proto::PrintJobInfo> print_job_info_protos) {
+    std::vector<base::Value> print_jobs) {
   std::vector<api::printing_metrics::PrintJobInfo> print_job_infos;
-  if (success) {
-    for (const auto& print_job_info_proto : print_job_info_protos)
-      print_job_infos.push_back(PrintJobInfoProtoToIdl(print_job_info_proto));
+  for (const auto& print_job : print_jobs) {
+    std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info =
+        api::printing_metrics::PrintJobInfo::FromValue(print_job);
+    DCHECK(print_job_info);
+    print_job_infos.emplace_back(std::move(*print_job_info));
   }
   Respond(ArgumentList(
       api::printing_metrics::GetPrintJobs::Results::Create(print_job_infos)));
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.h b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.h
index ae2b8511..5cd4f59 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.h
+++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api.h
@@ -7,10 +7,13 @@
 
 #include <vector>
 
-#include "chrome/browser/ash/printing/history/print_job_info.pb.h"
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/extension_function_histogram_value.h"
 
+namespace base {
+class Value;
+}  // namespace base
+
 namespace extensions {
 
 class PrintingMetricsGetPrintJobsFunction : public ExtensionFunction {
@@ -21,9 +24,8 @@
   ResponseAction Run() override;
 
  private:
-  void OnPrintJobsRetrieved(
-      bool success,
-      std::vector<ash::printing::proto::PrintJobInfo> print_job_info_protos);
+  void OnPrintJobsRetrieved(std::vector<base::Value> print_jobs);
+
   DECLARE_EXTENSION_FUNCTION("printingMetrics.getPrintJobs",
                              PRINTINGMETRICS_GETPRINTJOBS)
 };
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.cc b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.cc
new file mode 100644
index 0000000..474fbdf2
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.cc
@@ -0,0 +1,88 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.h"
+#include "base/logging.h"
+#include "chrome/common/extensions/api/printing_metrics.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/browser_context_keyed_api_factory.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_event_histogram_value.h"
+
+#include "chrome/browser/ash/crosapi/crosapi_ash.h"
+#include "chrome/browser/ash/crosapi/crosapi_manager.h"
+#include "chrome/browser/ash/crosapi/printing_metrics_ash.h"
+
+namespace extensions {
+
+// static
+BrowserContextKeyedAPIFactory<PrintingMetricsService>*
+PrintingMetricsService::GetFactoryInstance() {
+  static base::NoDestructor<
+      BrowserContextKeyedAPIFactory<PrintingMetricsService>>
+      instance;
+  return instance.get();
+}
+
+// static
+PrintingMetricsService* PrintingMetricsService::Get(
+    content::BrowserContext* context) {
+  return BrowserContextKeyedAPIFactory<PrintingMetricsService>::Get(context);
+}
+
+PrintingMetricsService::PrintingMetricsService(content::BrowserContext* context)
+    : context_(context) {
+  EventRouter::Get(context_)->RegisterObserver(
+      this, api::printing_metrics::OnPrintJobFinished::kEventName);
+}
+
+PrintingMetricsService::~PrintingMetricsService() = default;
+
+void PrintingMetricsService::Shutdown() {
+  EventRouter::Get(context_)->UnregisterObserver(this);
+}
+
+void PrintingMetricsService::OnListenerAdded(const EventListenerInfo&) {
+  // Ensures that the two-way crosapi connection is initialized for the profile,
+  // so that incoming onPrintJobFinished events can be dispatched to the
+  // extension.
+  EnsureInit();
+}
+
+void PrintingMetricsService::GetPrintJobs(
+    crosapi::mojom::PrintingMetricsForProfile::GetPrintJobsCallback callback) {
+  EnsureInit();
+  remote_->GetPrintJobs(std::move(callback));
+}
+
+void PrintingMetricsService::OnPrintJobFinished(base::Value print_job) {
+  std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info =
+      api::printing_metrics::PrintJobInfo::FromValue(std::move(print_job));
+  DCHECK(print_job_info);
+
+  auto event = std::make_unique<Event>(
+      events::PRINTING_METRICS_ON_PRINT_JOB_FINISHED,
+      api::printing_metrics::OnPrintJobFinished::kEventName,
+      api::printing_metrics::OnPrintJobFinished::Create(
+          std::move(*print_job_info)));
+
+  EventRouter::Get(context_)->BroadcastEvent(std::move(event));
+}
+
+void PrintingMetricsService::EnsureInit() {
+  if (initialized_) {
+    return;
+  }
+  initialized_ = true;
+
+  crosapi::PrintingMetricsAsh* printing_metrics_ash =
+      crosapi::CrosapiManager::Get()->crosapi_ash()->printing_metrics_ash();
+
+  printing_metrics_ash->RegisterForProfile(
+      Profile::FromBrowserContext(context_),
+      remote_.BindNewPipeAndPassReceiver(),
+      receiver_.BindNewPipeAndPassRemote());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.h b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.h
new file mode 100644
index 0000000..f8ea6038
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_service.h
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINTING_METRICS_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINTING_METRICS_SERVICE_H_
+
+#include "chromeos/crosapi/mojom/printing_metrics.mojom.h"
+#include "extensions/browser/browser_context_keyed_api_factory.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/event_router_factory.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace base {
+class Value;
+}  // namespace base
+
+namespace extensions {
+
+// This class implements the chrome.printingMetrics API.
+class PrintingMetricsService
+    : public BrowserContextKeyedAPI,
+      public EventRouter::Observer,
+      public crosapi::mojom::PrintJobObserverForProfile {
+ public:
+  static BrowserContextKeyedAPIFactory<PrintingMetricsService>*
+  GetFactoryInstance();
+
+  static PrintingMetricsService* Get(content::BrowserContext*);
+
+ public:
+  explicit PrintingMetricsService(content::BrowserContext*);
+  ~PrintingMetricsService() override;
+
+  PrintingMetricsService(const PrintingMetricsService&) = delete;
+  PrintingMetricsService& operator=(const PrintingMetricsService&) = delete;
+
+  // Proxies chrome.printingMetrics.getPrintJobs call to the remote.
+  void GetPrintJobs(
+      crosapi::mojom::PrintingMetricsForProfile::GetPrintJobsCallback);
+
+  // crosapi::mojom::PrintJobObserverForProfile:
+  void OnPrintJobFinished(base::Value print_job) override;
+
+ private:
+  friend class BrowserContextKeyedAPIFactory<PrintingMetricsService>;
+
+  // Services on the ash side are created in a lazy manner (i.e. when
+  // the user tries to access the API by calling a function or registering an
+  // observer). This function initializes the crosapi connection if it hasn't
+  // been settled yet.
+  void EnsureInit();
+
+  // BrowserContextKeyedAPI:
+  static const bool kServiceIsNULLWhileTesting = true;
+  static const char* service_name() { return "PrintingMetricsService"; }
+  void Shutdown() override;
+
+  // EventRouter::Observer:
+  void OnListenerAdded(const EventListenerInfo&) override;
+
+  raw_ptr<content::BrowserContext> context_;
+
+  bool initialized_ = false;
+
+  mojo::Remote<crosapi::mojom::PrintingMetricsForProfile> remote_;
+  mojo::Receiver<crosapi::mojom::PrintJobObserverForProfile> receiver_{this};
+};
+
+template <>
+struct BrowserContextFactoryDependencies<PrintingMetricsService> {
+  static void DeclareFactoryDependencies(
+      BrowserContextKeyedAPIFactory<PrintingMetricsService>* factory) {
+    factory->DependsOn(EventRouterFactory::GetInstance());
+  }
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_PRINTING_METRICS_PRINTING_METRICS_SERVICE_H_
diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc
index 9700c40..cfdc76a9 100644
--- a/chrome/browser/devtools/protocol/page_handler.cc
+++ b/chrome/browser/devtools/protocol/page_handler.cc
@@ -57,9 +57,6 @@
 
 protocol::Response PageHandler::Disable() {
   enabled_ = false;
-  // TODO(bokan): This is inadvertently called from a FencedFrame as it has a
-  // PageHandler that gets destroyed when the main frame is refreshed.
-  // ToggleAdBlocking should be a no-op for non-primary pages.
   ToggleAdBlocking(false /* enable */);
   SetSPCTransactionMode(protocol::Page::SetSPCTransactionMode::ModeEnum::None);
   // Do not mark the command as handled. Let it fall through instead, so that
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index 6ee24b74..d1c95bb1 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -565,9 +565,10 @@
 
   std::vector<GetAllFrames::Results::DetailsType> result_list;
 
-  // We currently do not expose back/forward cached frames in the GetAllFrames
-  // API, but we do explicitly include prerendered frames..
-  web_contents->ForEachRenderFrameHost(
+  // We only iterate the frames in the active page. We currently do not
+  // expose back/forward cached frames or prerender frames in the GetAllFrames
+  // API.
+  web_contents->GetPrimaryMainFrame()->ForEachRenderFrameHost(
       base::BindRepeating(
           [](content::WebContents* web_contents,
              std::vector<GetAllFrames::Results::DetailsType>& result_list,
@@ -587,13 +588,6 @@
               return content::RenderFrameHost::FrameIterationAction::kContinue;
             }
 
-            // Skip back/forward cached frames
-            if (render_frame_host->GetLifecycleState() ==
-                content::RenderFrameHost::LifecycleState::kInBackForwardCache) {
-              return content::RenderFrameHost::FrameIterationAction::
-                  kSkipChildren;
-            }
-
             GetAllFrames::Results::DetailsType frame;
             frame.url = navigation_state->GetUrl().spec();
             frame.frame_id =
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index 58440af..8feee12 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -248,13 +248,7 @@
     : public WebNavigationApiTest,
       public testing::WithParamInterface<ContextType> {
  public:
-  WebNavigationApiTestWithContextType() : WebNavigationApiTest(GetParam()) {
-    feature_list_.InitWithFeaturesAndParameters(
-        {
-            {blink::features::kPrerender2, {}},
-        },
-        {});
-  }
+  WebNavigationApiTestWithContextType() : WebNavigationApiTest(GetParam()) {}
   ~WebNavigationApiTestWithContextType() override = default;
   WebNavigationApiTestWithContextType(
       const WebNavigationApiTestWithContextType&) = delete;
@@ -267,9 +261,6 @@
     return RunExtensionTest(name, {},
                             {.allow_in_incognito = allow_in_incognito});
   }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, Api) {
@@ -277,7 +268,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, GetFrame) {
-  ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("webnavigation/getFrame")) << message_;
 }
 
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
index 9ef93f56..0d576b4 100644
--- a/chrome/browser/extensions/updater/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -1504,12 +1504,11 @@
     std::string hash;
     CRXFileInfo crx_file_info;
     base::Version version("0.0.1");
-    std::set<int> requests({0});
     ExtensionDownloaderTask task = CreateDownloaderTask(id);
     task.fetch_priority = fetch_priority;
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            std::move(task), test_url, hash, version.GetString(), requests,
+            std::move(task), test_url, hash, version.GetString(),
             fetch_priority);
 
     updater.downloader_->FetchUpdatedExtension(std::move(fetch), absl::nullopt);
@@ -1554,7 +1553,7 @@
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
             CreateDownloaderTask(id), test_url, hash, version.GetString(),
-            requests, DownloadFetchPriority::kBackground);
+            DownloadFetchPriority::kBackground);
     updater.downloader_->FetchUpdatedExtension(std::move(fetch), absl::nullopt);
 
     if (pending) {
@@ -1823,12 +1822,10 @@
     const std::string id(32, 'a');
     std::string hash;
     base::Version version("0.0.1");
-    std::set<int> requests;
-    requests.insert(0);
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> extension_fetch =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
             CreateDownloaderTask(id), test_url, hash, version.GetString(),
-            requests, DownloadFetchPriority::kBackground);
+            DownloadFetchPriority::kBackground);
     updater.downloader_->FetchUpdatedExtension(std::move(extension_fetch),
                                                absl::nullopt);
 
@@ -2044,16 +2041,14 @@
     std::string version1 = "0.1";
     std::string version2 = "0.1";
 
-    std::set<int> requests;
-    requests.insert(0);
     // Start two fetches
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch1 =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            CreateDownloaderTask(id1), url1, hash1, version1, requests,
+            CreateDownloaderTask(id1), url1, hash1, version1,
             DownloadFetchPriority::kBackground);
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch2 =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            CreateDownloaderTask(id2), url2, hash2, version2, requests,
+            CreateDownloaderTask(id2), url2, hash2, version2,
             DownloadFetchPriority::kBackground);
     updater.downloader_->FetchUpdatedExtension(std::move(fetch1),
                                                absl::optional<std::string>());
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc
index 8b0d71f0..14cfde4 100644
--- a/chrome/browser/feedback/show_feedback_page.cc
+++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -6,6 +6,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -49,6 +50,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 constexpr char kExtraDiagnosticsQueryParam[] = "extra_diagnostics";
+constexpr char kDescriptionTemplateQueryParam[] = "description_template";
 constexpr char kQueryParamSeparator[] = "&";
 constexpr char kQueryParamKeyValueSeparator[] = "=";
 
@@ -60,7 +62,8 @@
 }
 
 // Returns URL for OS Feedback with additional data passed as query parameters.
-GURL BuildFeedbackUrl(const std::string extra_diagnostics) {
+GURL BuildFeedbackUrl(const std::string extra_diagnostics,
+                      const std::string description_template) {
   std::vector<std::string> query_params;
 
   if (!extra_diagnostics.empty()) {
@@ -68,6 +71,11 @@
         StrCatQueryParam(kExtraDiagnosticsQueryParam, extra_diagnostics));
   }
 
+  if (!description_template.empty()) {
+    query_params.emplace_back(
+        StrCatQueryParam(kDescriptionTemplateQueryParam, description_template));
+  }
+
   // Use default URL if no extra parameters to be added.
   if (query_params.empty()) {
     return GURL(ash::kChromeUIOSFeedbackUrl);
@@ -155,7 +163,7 @@
   if (use_os_feedback) {
     web_app::SystemAppLaunchParams params{};
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-    params.url = BuildFeedbackUrl(extra_diagnostics);
+    params.url = BuildFeedbackUrl(extra_diagnostics, description_template);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     web_app::LaunchSystemWebAppAsync(
         profile, ash::SystemWebAppType::OS_FEEDBACK, std::move(params));
diff --git a/chrome/browser/feedback/show_feedback_page_browsertest.cc b/chrome/browser/feedback/show_feedback_page_browsertest.cc
index 3623463..c26f874 100644
--- a/chrome/browser/feedback/show_feedback_page_browsertest.cc
+++ b/chrome/browser/feedback/show_feedback_page_browsertest.cc
@@ -92,23 +92,28 @@
   EXPECT_EQ(expected_url, visible_url);
 }
 
-// Test that when `extra_diagnostics` parameter appended.
+// Test that when parameters appended include:
+// - `extra_diagnostics` string.
+// - `description_template` string.
 IN_PROC_BROWSER_TEST_F(ShowFeedbackPageBrowserTest,
                        OsFeedbackAdditionalContextAddedToUrl) {
   ash::SystemWebAppManager::GetForTest(browser()->profile())
       ->InstallSystemAppsForTesting();
   std::string unused;
   const std::string extra_diagnostics = "extra diagnostics param";
+  const std::string description_template = "Q1: Question one?";
   GURL expected_url(base::StrCat(
       {ash::kChromeUIOSFeedbackUrl, "/?extra_diagnostics=",
-       base::EscapeQueryParamValue(extra_diagnostics, /*use_plus=*/false)}));
+       base::EscapeQueryParamValue(extra_diagnostics, /*use_plus=*/false),
+       "&description_template=",
+       base::EscapeQueryParamValue(description_template, /*use_plus=*/false)}));
   content::TestNavigationObserver navigation_observer(expected_url);
   navigation_observer.StartWatchingNewWebContents();
 
   browser()->profile()->GetPrefs()->SetBoolean(prefs::kUserFeedbackAllowed,
                                                true);
   chrome::ShowFeedbackPage(browser(), chrome::kFeedbackSourceBrowserCommand,
-                           /*description_template=*/unused,
+                           /*description_template=*/description_template,
                            /*description_placeholder_text=*/unused,
                            /*category_tag=*/unused,
                            /*extra_diagnostics=*/extra_diagnostics);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 881b8b5..7db7892 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3102,6 +3102,11 @@
     "expiry_milestone": 98
   },
   {
+    "name": "enable-zram-writeback",
+    "owners": ["bgeffon@google.com", "chromeos-memory@google.com"],
+    "expiry_milestone": 118
+  },
+  {
     "name": "encrypted-client-hello",
     "owners": [ "davidben", "trusty-transport@chromium.org" ],
     "expiry_milestone": 114
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index ef8654e..e3ba6fc 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4845,6 +4845,11 @@
     "Shows touchscreen cards in the Diagnostics App's input section. Requires "
     "#enable-input-in-diagnostics-app to be enabled.";
 
+const char kEnableZramWriteback[] = "Enable Zram Writeback";
+const char kEnableZramWritebackDescription[] =
+    "If enabled zram swap will be able to write back to disk increasing "
+    "overall swap capacity";
+
 const char kEnableVariableRefreshRateName[] = "Enable Variable Refresh Rate";
 const char kEnableVariableRefreshRateDescription[] =
     "Enable the variable refresh rate (Adaptive Sync) setting for capable "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 3c32c83..042bdb9 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2766,6 +2766,9 @@
 extern const char kEnableTouchscreensInDiagnosticsAppName[];
 extern const char kEnableTouchscreensInDiagnosticsAppDescription[];
 
+extern const char kEnableZramWriteback[];
+extern const char kEnableZramWritebackDescription[];
+
 extern const char kDeprecateAssistantStylusFeaturesName[];
 extern const char kDeprecateAssistantStylusFeaturesDescription[];
 
diff --git a/chrome/browser/metrics/power/battery_level_provider.cc b/chrome/browser/metrics/power/battery_level_provider.cc
index 29f5648..aef0632 100644
--- a/chrome/browser/metrics/power/battery_level_provider.cc
+++ b/chrome/browser/metrics/power/battery_level_provider.cc
@@ -4,83 +4,31 @@
 
 #include "chrome/browser/metrics/power/battery_level_provider.h"
 
+#include "base/ranges/algorithm.h"
+
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
-BatteryLevelProvider::BatteryState::BatteryState(
-    size_t interface_count,
-    size_t battery_count,
-    absl::optional<double> charge_level,
-    bool on_battery,
-    base::TimeTicks capture_time)
-    : interface_count(interface_count),
-      battery_count(battery_count),
-      charge_level(charge_level),
-      on_battery(on_battery),
-      capture_time(capture_time) {}
-
-BatteryLevelProvider::BatteryState::BatteryState(const BatteryState&) = default;
-
-BatteryLevelProvider::BatteryState&
-BatteryLevelProvider::BatteryState::operator=(const BatteryState&) = default;
-
-BatteryLevelProvider::BatteryInterface::BatteryInterface(
-    bool battery_present_in)
-    : battery_present(battery_present_in) {}
-
-BatteryLevelProvider::BatteryInterface::BatteryInterface(
-    const BatteryDetails& details_in)
-    : battery_present(true), details(details_in) {}
-
-BatteryLevelProvider::BatteryInterface::BatteryInterface(
-    const BatteryInterface&) = default;
-
 BatteryLevelProvider::BatteryState BatteryLevelProvider::MakeBatteryState(
-    const std::vector<BatteryInterface>& battery_interfaces) {
-  const base::TimeTicks capture_time = base::TimeTicks::Now();
+    const std::vector<BatteryDetails>& battery_details) {
+  BatteryState state;
 
-  uint64_t total_max_capacity = 0;
-  uint64_t total_current_capacity = 0;
-  bool on_battery = true;
-  bool any_capacity_invalid = false;
-  size_t battery_count = 0;
+  state.battery_count = static_cast<int>(battery_details.size());
+  state.is_external_power_connected =
+      battery_details.size() == 0 ||
+      base::ranges::any_of(battery_details, [](const BatteryDetails& details) {
+        return details.is_external_power_connected;
+      });
+  state.current_capacity =
+      battery_details.size() == 1
+          ? absl::make_optional(battery_details.front().current_capacity)
+          : absl::nullopt;
+  state.full_charged_capacity =
+      battery_details.size() == 1
+          ? absl::make_optional(battery_details.front().full_charged_capacity)
+          : absl::nullopt;
+  state.capture_time = base::TimeTicks::Now();
 
-  for (auto& interface : battery_interfaces) {
-    // The interface might have no battery.
-    if (!interface.battery_present)
-      continue;
-
-    // Counts the number of interfaces that has |battery_present == true|.
-    ++battery_count;
-
-    // The state is considered on battery power only if all of the batteries
-    // are explicitly marked as not connected.
-    if (!interface.details.has_value() || interface.details->is_connected)
-      on_battery = false;
-
-    if (!interface.details.has_value()) {
-      any_capacity_invalid = true;
-      continue;
-    }
-
-    // Total capacity is averaged across all the batteries.
-    total_current_capacity += interface.details->current_capacity;
-    total_max_capacity += interface.details->full_charged_capacity;
-  }
-
-  absl::optional<double> charge_level;
-  // Avoid invalid division.
-  if (!any_capacity_invalid && total_max_capacity != 0) {
-    charge_level = static_cast<double>(total_current_capacity) /
-                   static_cast<double>(total_max_capacity);
-  }
-  // If no battery was detected, we consider the system to be drawing power
-  // from an external power source, which is different from |on_battery|'s
-  // default value.
-  if (battery_count == 0)
-    on_battery = false;
-
-  return {battery_interfaces.size(), battery_count, charge_level, on_battery,
-          capture_time};
+  return state;
 }
 
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
diff --git a/chrome/browser/metrics/power/battery_level_provider.h b/chrome/browser/metrics/power/battery_level_provider.h
index 62c918c..ba494e2 100644
--- a/chrome/browser/metrics/power/battery_level_provider.h
+++ b/chrome/browser/metrics/power/battery_level_provider.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_METRICS_POWER_BATTERY_LEVEL_PROVIDER_H_
 #define CHROME_BROWSER_METRICS_POWER_BATTERY_LEVEL_PROVIDER_H_
 
+#include <stdint.h>
 #include <memory>
 #include <vector>
 
@@ -26,29 +27,18 @@
   // Represents an aggregated state of all the batteries on the system at a
   // certain point in time.
   struct BatteryState {
-    BatteryState(size_t interface_count,
-                 size_t battery_count,
-                 absl::optional<double> charge_level,
-                 bool on_battery,
-                 base::TimeTicks capture_time);
-    BatteryState(const BatteryState&);
-    BatteryState& operator=(const BatteryState&);
+    // Number of batteries on the system.
+    int battery_count = 0;
 
-    // Number of device interfaces that accept a battery on the system.
-    size_t interface_count = 0;
+    // Whether the system is connected to an external source of power. Defaults
+    // to `true` if `battery_count` is 0.
+    bool is_external_power_connected = false;
 
-    // Number of batteries detected on the system.
-    size_t battery_count = 0;
+    // Current battery capacity. nullopt if `battery_count` != 1.
+    absl::optional<uint64_t> current_capacity;
 
-    // A fraction of the maximal battery capacity of the system, in the range
-    // [0.00, 1.00], or nullopt if no battery is present or querying charge
-    // level failed. This may be nullopt even if |on_battery == true|, which
-    // indicates a failure to grab the battery level.
-    absl::optional<double> charge_level = 0;
-
-    // True if the system is running on battery power, false if the system is
-    // drawing power from an external power source.
-    bool on_battery = false;
+    // Fully charged battery capacity. nullopt if `battery_count` != 1.
+    absl::optional<uint64_t> full_charged_capacity;
 
     // The time at which the battery state capture took place.
     base::TimeTicks capture_time;
@@ -63,18 +53,19 @@
   BatteryLevelProvider(const BatteryLevelProvider& other) = delete;
   BatteryLevelProvider& operator=(const BatteryLevelProvider& other) = delete;
 
-  // Queries the current battery state and returns it to |callback| when ready.
-  // |callback| will not be invoked if the BatteryLevelProvider is destroyed.
+  // Queries the current battery state and forwards it to `callback` when ready
+  // (forwards nullopt on retrieval error). `callback` will not be invoked if
+  // the BatteryLevelProvider is destroyed.
   virtual void GetBatteryState(
-      base::OnceCallback<void(const BatteryState&)> callback) = 0;
+      base::OnceCallback<void(const absl::optional<BatteryState>&)>
+          callback) = 0;
 
  protected:
   BatteryLevelProvider() = default;
 
   struct BatteryDetails {
-    // True if the battery is connected and drawing power from external power
-    // source.
-    bool is_connected;
+    // Whether the battery is connected to an external power source.
+    bool is_external_power_connected;
 
     // The current battery capacity.
     uint64_t current_capacity;
@@ -83,24 +74,10 @@
     uint64_t full_charged_capacity;
   };
 
-  struct BatteryInterface {
-    // Indicates whether a battery is present on the interface, without
-    // additional battery details.
-    explicit BatteryInterface(bool battery_present_in);
-    // Provides the details of a battery that was detected on the interface.
-    explicit BatteryInterface(const BatteryDetails& details_in);
-    BatteryInterface(const BatteryInterface&);
-
-    // True if a battery is detected.
-    const bool battery_present;
-
-    // Detailed power state of the battery. This may be nullopt even if
-    // |battery_present == true| when the details couldn't be queried.
-    const absl::optional<BatteryDetails> details;
-  };
-
+  // Constructs a `BatteryState` from a list of `BatteryDetails`. The list can
+  // be empty if there are no batteries on the system.
   static BatteryState MakeBatteryState(
-      const std::vector<BatteryInterface>& battery_interfaces);
+      const std::vector<BatteryDetails>& battery_details);
 };
 
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
diff --git a/chrome/browser/metrics/power/battery_level_provider_mac.mm b/chrome/browser/metrics/power/battery_level_provider_mac.mm
index 2379292..c198ab98 100644
--- a/chrome/browser/metrics/power/battery_level_provider_mac.mm
+++ b/chrome/browser/metrics/power/battery_level_provider_mac.mm
@@ -46,36 +46,62 @@
   ~BatteryLevelProviderMac() override = default;
 
   void GetBatteryState(
-      base::OnceCallback<void(const BatteryState&)> callback) override {
-    std::vector<BatteryInterface> battery_interfaces =
-        GetBatteryInterfaceList();
-    std::move(callback).Run(
-        BatteryLevelProvider::MakeBatteryState(battery_interfaces));
+      base::OnceCallback<void(const absl::optional<BatteryState>&)> callback)
+      override {
+    std::move(callback).Run(GetBatteryStateImpl());
   }
 
  private:
-  static std::vector<BatteryInterface> GetBatteryInterfaceList();
-
-  static BatteryLevelProvider::BatteryInterface GetInterface(
-      CFDictionaryRef description);
+  absl::optional<BatteryState> GetBatteryStateImpl();
 };
 
 std::unique_ptr<BatteryLevelProvider> BatteryLevelProvider::Create() {
   return std::make_unique<BatteryLevelProviderMac>();
 }
 
-BatteryLevelProvider::BatteryInterface BatteryLevelProviderMac::GetInterface(
-    CFDictionaryRef description) {
+absl::optional<BatteryLevelProviderMac::BatteryState>
+BatteryLevelProviderMac::GetBatteryStateImpl() {
+  const base::mac::ScopedIOObject<io_service_t> service(
+      IOServiceGetMatchingService(kIOMasterPortDefault,
+                                  IOServiceMatching("IOPMPowerSource")));
+  if (service == IO_OBJECT_NULL) {
+    // Macs without a battery don't necessarily provide the IOPMPowerSource
+    // service (e.g. test bots). Don't report this as an error.
+    return MakeBatteryState(/* battery_details=*/{});
+  }
+
+  base::ScopedCFTypeRef<CFMutableDictionaryRef> dict;
+  kern_return_t result = IORegistryEntryCreateCFProperties(
+      service.get(), dict.InitializeInto(), 0, 0);
+
+  if (result != KERN_SUCCESS) {
+    // Failing to retrieve the dictionary is unexpected.
+    return absl::nullopt;
+  }
+
+  absl::optional<bool> battery_installed =
+      GetValueAsBoolean(dict, CFSTR("BatteryInstalled"));
+  if (!battery_installed.has_value()) {
+    // Failing to access the BatteryInstalled property is unexpected.
+    return absl::nullopt;
+  }
+
+  if (!battery_installed.value()) {
+    // BatteryInstalled == false means that there is no battery.
+    return MakeBatteryState(/* battery_details=*/{});
+  }
+
   absl::optional<bool> external_connected =
-      GetValueAsBoolean(description, CFSTR("ExternalConnected"));
-  if (!external_connected.has_value())
-    return BatteryInterface(true);
-  bool is_connected = *external_connected;
+      GetValueAsBoolean(dict, CFSTR("ExternalConnected"));
+  if (!external_connected.has_value()) {
+    // Failing to access the ExternalConnected property is unexpected.
+    return absl::nullopt;
+  }
 
   CFStringRef capacity_key;
   CFStringRef max_capacity_key;
 
-  // Use the correct key depending on macOS version.
+  // Use the correct capacity keys depending on macOS version.
   if (@available(macOS 10.14.0, *)) {
     capacity_key = CFSTR("AppleRawCurrentCapacity");
     max_capacity_key = CFSTR("AppleRawMaxCapacity");
@@ -84,40 +110,23 @@
     max_capacity_key = CFSTR("RawMaxCapacity");
   }
 
-  // Extract the information from the dictionary.
   absl::optional<SInt64> current_capacity =
-      GetValueAsSInt64(description, capacity_key);
+      GetValueAsSInt64(dict, capacity_key);
+  if (!current_capacity.has_value()) {
+    return absl::nullopt;
+  }
+
   absl::optional<SInt64> max_capacity =
-      GetValueAsSInt64(description, max_capacity_key);
-  if (!current_capacity.has_value() || !max_capacity.has_value())
-    return BatteryInterface(true);
+      GetValueAsSInt64(dict, max_capacity_key);
+  if (!max_capacity.has_value()) {
+    return absl::nullopt;
+  }
+
   DCHECK_GE(*current_capacity, 0);
   DCHECK_GE(*max_capacity, 0);
-  return BatteryInterface({is_connected,
-                           static_cast<uint64_t>(*current_capacity),
-                           static_cast<uint64_t>(*max_capacity)});
-}
 
-std::vector<BatteryLevelProvider::BatteryInterface>
-BatteryLevelProviderMac::GetBatteryInterfaceList() {
-  // Retrieve the IOPMPowerSource service.
-  const base::mac::ScopedIOObject<io_service_t> service(
-      IOServiceGetMatchingService(kIOMasterPortDefault,
-                                  IOServiceMatching("IOPMPowerSource")));
-  if (service == IO_OBJECT_NULL)
-    return {};
-
-  // Gather a dictionary containing the power information.
-  base::ScopedCFTypeRef<CFMutableDictionaryRef> dict;
-  kern_return_t result = IORegistryEntryCreateCFProperties(
-      service.get(), dict.InitializeInto(), 0, 0);
-
-  std::vector<BatteryInterface> interfaces;
-  // Retrieving dictionary failed. Cannot proceed.
-  if (result != KERN_SUCCESS) {
-    interfaces.push_back(BatteryInterface(false));
-  } else {
-    interfaces.push_back(GetInterface(dict));
-  }
-  return interfaces;
+  return MakeBatteryState({BatteryDetails{
+      .is_external_power_connected = external_connected.value(),
+      .current_capacity = static_cast<uint64_t>(current_capacity.value()),
+      .full_charged_capacity = static_cast<uint64_t>(max_capacity.value())}});
 }
diff --git a/chrome/browser/metrics/power/battery_level_provider_unittest.cc b/chrome/browser/metrics/power/battery_level_provider_unittest.cc
index 5aa2db5..15e29d5 100644
--- a/chrome/browser/metrics/power/battery_level_provider_unittest.cc
+++ b/chrome/browser/metrics/power/battery_level_provider_unittest.cc
@@ -12,103 +12,81 @@
 
 class FakeBatteryLevelProvider : public BatteryLevelProvider {
  public:
-  using BatteryInterface = BatteryLevelProvider::BatteryInterface;
+  using BatteryDetails = BatteryLevelProvider::BatteryDetails;
 
   FakeBatteryLevelProvider() = default;
 
   static BatteryState MakeBatteryState(
-      const std::vector<BatteryInterface>& battery_interfaces) {
-    return BatteryLevelProvider::MakeBatteryState(battery_interfaces);
+      const std::vector<BatteryDetails>& battery_details) {
+    return BatteryLevelProvider::MakeBatteryState(battery_details);
   }
 };
 
 }  // namespace
 
-using BatteryInterface = FakeBatteryLevelProvider::BatteryInterface;
-
-TEST(BatteryLevelProviderTest, NoInterface) {
-  auto state = FakeBatteryLevelProvider::MakeBatteryState({});
-  EXPECT_EQ(0U, state.interface_count);
-  EXPECT_EQ(0U, state.battery_count);
-  EXPECT_FALSE(state.charge_level.has_value());
-  EXPECT_FALSE(state.on_battery);
-}
+using BatteryDetails = FakeBatteryLevelProvider::BatteryDetails;
 
 TEST(BatteryLevelProviderTest, NoBattery) {
-  auto state =
-      FakeBatteryLevelProvider::MakeBatteryState({BatteryInterface(false)});
-  EXPECT_EQ(1U, state.interface_count);
-  EXPECT_EQ(0U, state.battery_count);
-  EXPECT_FALSE(state.charge_level.has_value());
-  EXPECT_FALSE(state.on_battery);
+  auto state = FakeBatteryLevelProvider::MakeBatteryState({});
+  EXPECT_EQ(0, state.battery_count);
+  EXPECT_TRUE(state.is_external_power_connected);
+  EXPECT_FALSE(state.current_capacity.has_value());
+  EXPECT_FALSE(state.full_charged_capacity.has_value());
+  EXPECT_NE(base::TimeTicks(), state.capture_time);
 }
 
-TEST(BatteryLevelProviderTest, PluggedBattery) {
+TEST(BatteryLevelProviderTest, SingleBatteryWithExternalPower) {
   auto state = FakeBatteryLevelProvider::MakeBatteryState(
-      {BatteryInterface({true, 42, 100})});
-  EXPECT_EQ(1U, state.interface_count);
-  EXPECT_EQ(1U, state.battery_count);
-  ASSERT_TRUE(state.charge_level.has_value());
-  EXPECT_EQ(0.42, *state.charge_level);
-  EXPECT_FALSE(state.on_battery);
+      {BatteryDetails({.is_external_power_connected = true,
+                       .current_capacity = 42,
+                       .full_charged_capacity = 100})});
+  EXPECT_EQ(1, state.battery_count);
+  EXPECT_TRUE(state.is_external_power_connected);
+  EXPECT_EQ(42U, state.current_capacity);
+  EXPECT_EQ(100U, state.full_charged_capacity);
+  EXPECT_NE(base::TimeTicks(), state.capture_time);
 }
 
-TEST(BatteryLevelProviderTest, DischargingBattery) {
+TEST(BatteryLevelProviderTest, SingleBatteryDischarging) {
   auto state = FakeBatteryLevelProvider::MakeBatteryState(
-      {BatteryInterface({false, 42, 100})});
-  EXPECT_EQ(1U, state.interface_count);
-  EXPECT_EQ(1U, state.battery_count);
-  ASSERT_TRUE(state.charge_level.has_value());
-  EXPECT_EQ(0.42, *state.charge_level);
-  EXPECT_TRUE(state.on_battery);
+      {BatteryDetails({.is_external_power_connected = false,
+                       .current_capacity = 42,
+                       .full_charged_capacity = 100})});
+  EXPECT_EQ(1, state.battery_count);
+  EXPECT_FALSE(state.is_external_power_connected);
+  EXPECT_EQ(42U, state.current_capacity);
+  EXPECT_EQ(100U, state.full_charged_capacity);
+  EXPECT_NE(base::TimeTicks(), state.capture_time);
 }
 
-TEST(BatteryLevelProviderTest, InvalidBattery) {
-  auto state =
-      FakeBatteryLevelProvider::MakeBatteryState({BatteryInterface(true)});
-  EXPECT_EQ(1U, state.interface_count);
-  EXPECT_EQ(1U, state.battery_count);
-  EXPECT_FALSE(state.charge_level.has_value());
-  EXPECT_FALSE(state.on_battery);
-}
-
-TEST(BatteryLevelProviderTest, MultipleInterfaces) {
+TEST(BatteryLevelProviderTest, MultipleBatteriesWithExternalPower) {
   auto state = FakeBatteryLevelProvider::MakeBatteryState(
-      {BatteryInterface(false), BatteryInterface({false, 42, 100})});
-  EXPECT_EQ(2U, state.interface_count);
-  EXPECT_EQ(1U, state.battery_count);
-  ASSERT_TRUE(state.charge_level.has_value());
-  EXPECT_EQ(0.42, *state.charge_level);
-  EXPECT_TRUE(state.on_battery);
-}
-
-TEST(BatteryLevelProviderTest, MultipleBatteries) {
-  auto state = FakeBatteryLevelProvider::MakeBatteryState(
-      {BatteryInterface({true, 10, 100}), BatteryInterface({false, 30, 100})});
-  EXPECT_EQ(2U, state.interface_count);
-  EXPECT_EQ(2U, state.battery_count);
-  ASSERT_TRUE(state.charge_level.has_value());
-  EXPECT_EQ(0.20, *state.charge_level);
-  EXPECT_FALSE(state.on_battery);
+      {BatteryDetails({.is_external_power_connected = false,
+                       .current_capacity = 42,
+                       .full_charged_capacity = 100}),
+       BatteryDetails({.is_external_power_connected = true,
+                       .current_capacity = 10,
+                       .full_charged_capacity = 100})});
+  EXPECT_EQ(2, state.battery_count);
+  EXPECT_TRUE(state.is_external_power_connected);
+  EXPECT_EQ(absl::nullopt, state.current_capacity);
+  EXPECT_EQ(absl::nullopt, state.full_charged_capacity);
+  EXPECT_NE(base::TimeTicks(), state.capture_time);
 }
 
 TEST(BatteryLevelProviderTest, MultipleBatteriesDischarging) {
   auto state = FakeBatteryLevelProvider::MakeBatteryState(
-      {BatteryInterface({false, 10, 100}), BatteryInterface({false, 30, 100})});
-  EXPECT_EQ(2U, state.interface_count);
-  EXPECT_EQ(2U, state.battery_count);
-  ASSERT_TRUE(state.charge_level.has_value());
-  EXPECT_EQ(0.20, *state.charge_level);
-  EXPECT_TRUE(state.on_battery);
-}
-
-TEST(BatteryLevelProviderTest, MultipleBatteriesInvalid) {
-  auto state = FakeBatteryLevelProvider::MakeBatteryState(
-      {BatteryInterface(true), BatteryInterface({false, 10, 100})});
-  EXPECT_EQ(2U, state.interface_count);
-  EXPECT_EQ(2U, state.battery_count);
-  EXPECT_FALSE(state.charge_level.has_value());
-  EXPECT_FALSE(state.on_battery);
+      {BatteryDetails({.is_external_power_connected = false,
+                       .current_capacity = 42,
+                       .full_charged_capacity = 100}),
+       BatteryDetails({.is_external_power_connected = false,
+                       .current_capacity = 10,
+                       .full_charged_capacity = 100})});
+  EXPECT_EQ(2, state.battery_count);
+  EXPECT_FALSE(state.is_external_power_connected);
+  EXPECT_EQ(absl::nullopt, state.current_capacity);
+  EXPECT_EQ(absl::nullopt, state.full_charged_capacity);
+  EXPECT_NE(base::TimeTicks(), state.capture_time);
 }
 
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
diff --git a/chrome/browser/metrics/power/battery_level_provider_win.cc b/chrome/browser/metrics/power/battery_level_provider_win.cc
index c257f16..bf1b356d 100644
--- a/chrome/browser/metrics/power/battery_level_provider_win.cc
+++ b/chrome/browser/metrics/power/battery_level_provider_win.cc
@@ -59,11 +59,10 @@
   return battery;
 }
 
-// Returns the current tag for |battery| handle, or nullopt if there is no
-// battery at this slot or the request failed. Each battery in a particular slot
-// is assigned a tag, which must be used for all queries for information. For
-// more details, see
-// https://docs.microsoft.com/en-us/windows/win32/power/battery-information
+// Returns the current tag for `battery` handle, BATTERY_TAG_INVALID if there is
+// no battery present in this interface or nullopt on retrieval error.
+// See
+// https://docs.microsoft.com/en-us/windows/win32/power/ioctl-battery-query-tag
 absl::optional<uint64_t> GetBatteryTag(HANDLE battery) {
   ULONG battery_tag = 0;
   ULONG wait = 0;
@@ -71,8 +70,18 @@
   BOOL success = ::DeviceIoControl(
       battery, IOCTL_BATTERY_QUERY_TAG, &wait, sizeof(wait), &battery_tag,
       sizeof(battery_tag), &bytes_returned, nullptr);
-  if (!success)
+  if (!success) {
+    if (::GetLastError() == ERROR_FILE_NOT_FOUND) {
+      // No battery present in this interface.
+      //
+      // TODO(crbug.com/1191045): Change CHECK to DCHECK in October 2022 after
+      // verifying that there are no crash reports.
+      CHECK_EQ(battery_tag, static_cast<ULONG>(BATTERY_TAG_INVALID));
+      return battery_tag;
+    }
+    // Retrieval error.
     return absl::nullopt;
+  }
   return battery_tag;
 }
 
@@ -121,33 +130,27 @@
   ~BatteryLevelProviderWin() override = default;
 
   void GetBatteryState(
-      base::OnceCallback<void(const BatteryState&)> callback) override {
-    // This is run on |blocking_task_runner_| since GetBatteryInterfaceList()
-    // has blocking calls and can take up to several seconds to complete.
+      base::OnceCallback<void(const absl::optional<BatteryState>&)> callback)
+      override {
+    // This is run on |blocking_task_runner_| since `GetBatteryStateImpl()` has
+    // blocking calls and can take several seconds to complete.
     blocking_task_runner_->PostTaskAndReplyWithResult(
-        FROM_HERE, base::BindOnce([]() {
-          std::vector<BatteryInterface> battery_interfaces =
-              GetBatteryInterfaceList();
-          return BatteryLevelProvider::MakeBatteryState(battery_interfaces);
-        }),
+        FROM_HERE,
+        base::BindOnce(&BatteryLevelProviderWin::GetBatteryStateImpl),
         base::BindOnce(&BatteryLevelProviderWin::OnBatteryStateObtained,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
  private:
-  static std::vector<BatteryInterface> GetBatteryInterfaceList();
-
-  static BatteryInterface GetInterface(
-      HDEVINFO devices,
-      SP_DEVICE_INTERFACE_DATA* interface_data);
+  static absl::optional<BatteryState> GetBatteryStateImpl();
 
   void OnBatteryStateObtained(
-      base::OnceCallback<void(const BatteryState&)> callback,
-      const BatteryState& battery_state) {
+      base::OnceCallback<void(const absl::optional<BatteryState>&)> callback,
+      const absl::optional<BatteryState>& battery_state) {
     std::move(callback).Run(battery_state);
   }
 
-  // TaskRunner used to run blocking GetBatteryInterfaceList queries, sequenced
+  // TaskRunner used to run blocking `GetBatteryStateImpl()` queries, sequenced
   // to avoid the performance cost of concurrent calls.
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_{
     base::ThreadPool::CreateSequencedTaskRunner(
@@ -160,29 +163,9 @@
   return std::make_unique<BatteryLevelProviderWin>();
 }
 
-BatteryLevelProvider::BatteryInterface BatteryLevelProviderWin::GetInterface(
-    HDEVINFO devices,
-    SP_DEVICE_INTERFACE_DATA* interface_data) {
-  base::win::ScopedHandle battery = GetBatteryHandle(devices, interface_data);
-  if (!battery.IsValid())
-    return BatteryInterface(false);
-
-  absl::optional<uint64_t> battery_tag = GetBatteryTag(battery.Get());
-  if (!battery_tag)
-    return BatteryInterface(false);
-  auto battery_information = GetBatteryInformation(battery.Get(), *battery_tag);
-  auto battery_status = GetBatteryStatus(battery.Get(), *battery_tag);
-  // If any of the values were not available.
-  if (!battery_information.has_value() || !battery_status.has_value())
-    return BatteryInterface(true);
-
-  return BatteryInterface(
-      {!!(battery_status->PowerState & BATTERY_POWER_ON_LINE),
-       battery_status->Capacity, battery_information->FullChargedCapacity});
-}
-
-std::vector<BatteryLevelProvider::BatteryInterface>
-BatteryLevelProviderWin::GetBatteryInterfaceList() {
+// static
+absl::optional<BatteryLevelProvider::BatteryState>
+BatteryLevelProviderWin::GetBatteryStateImpl() {
   // Proactively mark as blocking to fail early, since calls below may also
   // trigger ScopedBlockingCall.
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
@@ -193,10 +176,11 @@
   // disconnected.
   base::win::ScopedDevInfo devices(::SetupDiGetClassDevs(
       &GUID_DEVICE_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
-  if (!devices.is_valid())
-    return {};
+  if (!devices.is_valid()) {
+    return absl::nullopt;
+  }
 
-  std::vector<BatteryInterface> interfaces;
+  std::vector<BatteryDetails> battery_details_list;
 
   // The algorithm to enumerate battery devices is taken from
   // https://docs.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
@@ -210,13 +194,43 @@
         ::SetupDiEnumDeviceInterfaces(devices.get(), 0, &GUID_DEVCLASS_BATTERY,
                                       device_index, &interface_data);
     if (!success) {
-      // Exit condition.
-      if (ERROR_NO_MORE_ITEMS == ::GetLastError())
+      // Enumeration ended normally.
+      if (::GetLastError() == ERROR_NO_MORE_ITEMS)
         break;
+      // Error.
+      return absl::nullopt;
+    }
+
+    base::win::ScopedHandle battery =
+        GetBatteryHandle(devices.get(), &interface_data);
+    if (!battery.IsValid())
+      return absl::nullopt;
+
+    absl::optional<uint64_t> battery_tag = GetBatteryTag(battery.Get());
+    if (!battery_tag.has_value()) {
+      return absl::nullopt;
+    } else if (battery_tag.value() == BATTERY_TAG_INVALID) {
+      // No battery present in this interface.
       continue;
     }
 
-    interfaces.push_back(GetInterface(devices.get(), &interface_data));
+    auto battery_information =
+        GetBatteryInformation(battery.Get(), *battery_tag);
+    if (!battery_information.has_value()) {
+      return absl::nullopt;
+    }
+
+    auto battery_status = GetBatteryStatus(battery.Get(), *battery_tag);
+    if (!battery_status.has_value()) {
+      return absl::nullopt;
+    }
+
+    battery_details_list.push_back(BatteryDetails(
+        {.is_external_power_connected =
+             !!(battery_status->PowerState & BATTERY_POWER_ON_LINE),
+         .current_capacity = battery_status->Capacity,
+         .full_charged_capacity = battery_information->FullChargedCapacity}));
   }
-  return interfaces;
+
+  return MakeBatteryState(battery_details_list);
 }
diff --git a/chrome/browser/metrics/power/power_metrics.cc b/chrome/browser/metrics/power/power_metrics.cc
index 6d61f39a..8d852da 100644
--- a/chrome/browser/metrics/power/power_metrics.cc
+++ b/chrome/browser/metrics/power/power_metrics.cc
@@ -20,7 +20,7 @@
 constexpr const char* kBatteryDischargeRateHistogramName =
     "Power.BatteryDischargeRate2";
 constexpr const char* kBatteryDischargeModeHistogramName =
-    "Power.BatteryDischargeMode";
+    "Power.BatteryDischargeMode2";
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
 #if BUILDFLAG(IS_MAC)
@@ -65,22 +65,50 @@
 
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 BatteryDischarge GetBatteryDischargeDuringInterval(
-    const BatteryLevelProvider::BatteryState& previous_battery_state,
-    const BatteryLevelProvider::BatteryState& new_battery_state,
+    const absl::optional<BatteryLevelProvider::BatteryState>&
+        previous_battery_state,
+    const absl::optional<BatteryLevelProvider::BatteryState>& new_battery_state,
     base::TimeDelta interval_duration) {
-  if (previous_battery_state.battery_count == 0 ||
-      new_battery_state.battery_count == 0) {
-    return {BatteryDischargeMode::kNoBattery, absl::nullopt};
+  if (!previous_battery_state.has_value() || !new_battery_state.has_value()) {
+    return {BatteryDischargeMode::kRetrievalError, absl::nullopt};
   }
-  if (!previous_battery_state.on_battery && !new_battery_state.on_battery) {
-    return {BatteryDischargeMode::kPluggedIn, absl::nullopt};
-  }
-  if (previous_battery_state.on_battery != new_battery_state.on_battery) {
+  if (previous_battery_state->is_external_power_connected !=
+          new_battery_state->is_external_power_connected ||
+      previous_battery_state->battery_count !=
+          new_battery_state->battery_count) {
     return {BatteryDischargeMode::kStateChanged, absl::nullopt};
   }
-  if (!previous_battery_state.charge_level.has_value() ||
-      !new_battery_state.charge_level.has_value()) {
-    return {BatteryDischargeMode::kChargeLevelUnavailable, absl::nullopt};
+  if (new_battery_state->battery_count == 0) {
+    return {BatteryDischargeMode::kNoBattery, absl::nullopt};
+  }
+  if (new_battery_state->is_external_power_connected) {
+    return {BatteryDischargeMode::kPluggedIn, absl::nullopt};
+  }
+  if (new_battery_state->battery_count > 1) {
+    return {BatteryDischargeMode::kMultipleBatteries, absl::nullopt};
+  }
+
+  // TODO(crbug.com/1191045): Change CHECK to DCHECK in October 2022 after
+  // verifying that there are no crash reports.
+  CHECK(previous_battery_state->current_capacity.has_value());
+  CHECK(previous_battery_state->full_charged_capacity.has_value());
+  CHECK(new_battery_state->current_capacity.has_value());
+  CHECK(new_battery_state->full_charged_capacity.has_value());
+
+#if BUILDFLAG(IS_MAC)
+  // On MacOS, empirical evidence has shown that right after a full charge, the
+  // current capacity stays equal to the maximum capacity for several minutes,
+  // despite the fact that power was definitely consumed. Reporting a zero
+  // discharge rate for this duration would be misleading.
+  if (previous_battery_state->current_capacity ==
+      previous_battery_state->full_charged_capacity) {
+    return {BatteryDischargeMode::kMacFullyCharged, absl::nullopt};
+  }
+#endif
+
+  if (previous_battery_state->full_charged_capacity.value() == 0 ||
+      new_battery_state->full_charged_capacity.value() == 0) {
+    return {BatteryDischargeMode::kFullChargedCapacityIsZero, absl::nullopt};
   }
 
   // The battery discharge rate is reported per minute with 1/10000 of full
@@ -88,18 +116,15 @@
   static constexpr int64_t kDischargeRateFactor =
       10000 * base::Minutes(1).InSecondsF();
 
-#if BUILDFLAG(IS_MAC)
-  // On MacOS, empirical evidence has shown that right after a full charge, the
-  // current capacity stays equal to the maximum capacity for several minutes,
-  // despite the fact that power was definitely consumed. Reporting a zero
-  // discharge rate for this duration would be misleading.
-  if (previous_battery_state.charge_level.value() == 1.0)
-    return {BatteryDischargeMode::kMacFullyCharged, absl::nullopt};
-#endif
-
-  auto discharge_rate = (previous_battery_state.charge_level.value() -
-                         new_battery_state.charge_level.value()) *
-                        kDischargeRateFactor / interval_duration.InSeconds();
+  const double previous_level =
+      static_cast<double>(previous_battery_state->current_capacity.value()) /
+      previous_battery_state->full_charged_capacity.value();
+  const double new_level =
+      static_cast<double>(new_battery_state->current_capacity.value()) /
+      new_battery_state->full_charged_capacity.value();
+  const double discharge_rate = (previous_level - new_level) *
+                                kDischargeRateFactor /
+                                interval_duration.InSeconds();
   if (discharge_rate < 0)
     return {BatteryDischargeMode::kBatteryLevelIncreased, absl::nullopt};
   return {BatteryDischargeMode::kDischarging, discharge_rate};
diff --git a/chrome/browser/metrics/power/power_metrics.h b/chrome/browser/metrics/power/power_metrics.h
index 5ea1162..24fb77568 100644
--- a/chrome/browser/metrics/power/power_metrics.h
+++ b/chrome/browser/metrics/power/power_metrics.h
@@ -29,12 +29,14 @@
   kDischarging = 0,
   kPluggedIn = 1,
   kStateChanged = 2,
-  kChargeLevelUnavailable = 3,
+  kRetrievalError = 3,
   kNoBattery = 4,
   kBatteryLevelIncreased = 5,
   kInvalidInterval = 6,
   kMacFullyCharged = 7,
-  kMaxValue = kMacFullyCharged
+  kMultipleBatteries = 8,
+  kFullChargedCapacityIsZero = 9,
+  kMaxValue = kFullChargedCapacityIsZero
 };
 
 struct BatteryDischarge {
@@ -48,8 +50,9 @@
 // If the discharge rate isn't valid, the returned rate is nullopt and the
 // reason is indicated per BatteryDischargeMode.
 BatteryDischarge GetBatteryDischargeDuringInterval(
-    const BatteryLevelProvider::BatteryState& previous_battery_state,
-    const BatteryLevelProvider::BatteryState& new_battery_state,
+    const absl::optional<BatteryLevelProvider::BatteryState>&
+        previous_battery_state,
+    const absl::optional<BatteryLevelProvider::BatteryState>& new_battery_state,
     base::TimeDelta interval_duration);
 
 // Report battery metrics to histograms with |suffixes|.
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.cc b/chrome/browser/metrics/power/power_metrics_reporter.cc
index 41bc864d..16e4352 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter.cc
@@ -152,7 +152,7 @@
 
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 void PowerMetricsReporter::OnFirstBatteryStateSampled(
-    const BatteryLevelProvider::BatteryState& battery_state) {
+    const absl::optional<BatteryLevelProvider::BatteryState>& battery_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   battery_state_ = battery_state;
 }
@@ -230,7 +230,8 @@
     const ProcessMonitor::Metrics& aggregated_process_metrics,
     base::TimeDelta interval_duration,
     base::TimeTicks battery_sample_begin_time,
-    const BatteryLevelProvider::BatteryState& new_battery_state) {
+    const absl::optional<BatteryLevelProvider::BatteryState>&
+        new_battery_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Report time it took to sample the battery state.
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.h b/chrome/browser/metrics/power/power_metrics_reporter.h
index 9db9402..52f1186 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.h
+++ b/chrome/browser/metrics/power/power_metrics_reporter.h
@@ -73,7 +73,8 @@
   ~PowerMetricsReporter() override;
 
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
-  BatteryLevelProvider::BatteryState& battery_state_for_testing() {
+  absl::optional<BatteryLevelProvider::BatteryState>&
+  battery_state_for_testing() {
     return battery_state_;
   }
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
@@ -84,7 +85,7 @@
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
   // Called when the initial battery state is obtained.
   void OnFirstBatteryStateSampled(
-      const BatteryLevelProvider::BatteryState& battery_state);
+      const absl::optional<BatteryLevelProvider::BatteryState>& battery_state);
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
   // Starts the timer for the long interval. On Mac, this will fire for the
@@ -113,7 +114,8 @@
       const ProcessMonitor::Metrics& aggregated_process_metrics,
       base::TimeDelta interval_duration,
       base::TimeTicks battery_sample_begin_time,
-      const BatteryLevelProvider::BatteryState& new_battery_state);
+      const absl::optional<BatteryLevelProvider::BatteryState>&
+          new_battery_state);
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
   // Called when the long interval (and the short one on Mac) ends.
@@ -156,8 +158,7 @@
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
   std::unique_ptr<BatteryLevelProvider> battery_level_provider_;
 
-  BatteryLevelProvider::BatteryState battery_state_{0, 0, absl::nullopt, false,
-                                                    base::TimeTicks::Now()};
+  absl::optional<BatteryLevelProvider::BatteryState> battery_state_;
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
   base::TimeTicks interval_begin_;
diff --git a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
index 6348af1..948f0f4 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
@@ -33,10 +33,20 @@
 constexpr const char* kBatteryDischargeRateHistogramName =
     "Power.BatteryDischargeRate2";
 constexpr const char* kBatteryDischargeModeHistogramName =
-    "Power.BatteryDischargeMode";
+    "Power.BatteryDischargeMode2";
 
 constexpr double kTolerableTimeElapsedRatio = 0.10;
 constexpr double kTolerablePositiveDrift = 1 + kTolerableTimeElapsedRatio;
+
+BatteryLevelProvider::BatteryState MakeBatteryDischarginState(
+    int battery_percent) {
+  return BatteryLevelProvider::BatteryState{
+      .battery_count = 1,
+      .is_external_power_connected = false,
+      .current_capacity = battery_percent,
+      .full_charged_capacity = 100,
+      .capture_time = base::TimeTicks::Now()};
+}
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
 ProcessMonitor::Metrics GetFakeProcessMetrics() {
@@ -74,19 +84,22 @@
 class FakeBatteryLevelProvider : public BatteryLevelProvider {
  public:
   explicit FakeBatteryLevelProvider(
-      std::queue<BatteryLevelProvider::BatteryState>* battery_states)
+      std::queue<absl::optional<BatteryLevelProvider::BatteryState>>*
+          battery_states)
       : battery_states_(battery_states) {}
 
   void GetBatteryState(
-      base::OnceCallback<void(const BatteryState&)> callback) override {
+      base::OnceCallback<void(const absl::optional<BatteryState>&)> callback)
+      override {
     DCHECK(!battery_states_->empty());
-    BatteryLevelProvider::BatteryState state = battery_states_->front();
+    auto state = battery_states_->front();
     battery_states_->pop();
     std::move(callback).Run(state);
   }
 
  private:
-  raw_ptr<std::queue<BatteryLevelProvider::BatteryState>> battery_states_;
+  raw_ptr<std::queue<absl::optional<BatteryLevelProvider::BatteryState>>>
+      battery_states_;
 };
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
@@ -158,8 +171,7 @@
   void SetUp() override {
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
     // Start with a half-full battery
-    battery_states_.push(BatteryLevelProvider::BatteryState{
-        1, 1, 0.5, true, base::TimeTicks::Now()});
+    battery_states_.push(MakeBatteryDischarginState(50));
     auto battery_provider =
         std::make_unique<FakeBatteryLevelProvider>(&battery_states_);
     battery_provider_ = battery_provider.get();
@@ -205,7 +217,8 @@
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
   ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
 
-  std::queue<BatteryLevelProvider::BatteryState> battery_states_;
+  std::queue<absl::optional<BatteryLevelProvider::BatteryState>>
+      battery_states_;
   raw_ptr<BatteryLevelProvider> battery_provider_;
 #endif  // HAS_BATTERY_LEVEL_PROVIDER_IMPL()
 
@@ -221,10 +234,8 @@
 
 TEST_F(PowerMetricsReporterUnitTest, LongIntervalHistograms) {
   process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
-
 #if HAS_BATTERY_LEVEL_PROVIDER_IMPL()
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.30, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(30));
 #endif
 
   UsageScenarioDataStore::IntervalData interval_data;
@@ -244,9 +255,7 @@
 #if BUILDFLAG(IS_MAC)
 TEST_F(PowerMetricsReporterUnitTest, ResourceCoalitionHistograms_EndToEnd) {
   process_monitor_.SetMetricsToReturn({});
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.30, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(30));
 
   UsageScenarioDataStore::IntervalData interval_data;
   interval_data.max_tab_count = 1;
@@ -283,8 +292,7 @@
   process_monitor_.SetMetricsToReturn(aggregated_process_metrics);
 
   // Pretend that the battery has dropped by 2%.
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.48, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(48));
 
   const base::TimeDelta kTooLate =
       kLongPowerMetricsIntervalDuration * kTolerablePositiveDrift +
@@ -303,9 +311,7 @@
   process_monitor_.SetMetricsToReturn(aggregated_process_metrics);
 
   // Pretend that the battery has dropped by 2%.
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.48, true, base::TimeTicks::Now()});
-  ;
+  battery_states_.push(MakeBatteryDischarginState(48));
 
   const base::TimeDelta kLate =
       kLongPowerMetricsIntervalDuration * kTolerablePositiveDrift -
@@ -332,8 +338,7 @@
 
   // Pretend that the battery has dropped by 20% in 2 minutes, for a rate of
   // 10% per minute.
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.30, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(30));
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.uptime_at_interval_end = base::Hours(++fake_value);
@@ -461,9 +466,7 @@
   fake_metrics.energy_impact = 44;
 #endif
   process_monitor_.SetMetricsToReturn(fake_metrics);
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.50, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(50));
 
   const ukm::SourceId kTestSourceId =
       ukm::ConvertToSourceId(42, ukm::SourceIdType::NAVIGATION_ID);
@@ -492,14 +495,19 @@
 TEST_F(PowerMetricsReporterUnitTest, UKMsPluggedIn) {
   // Update the latest reported battery state to pretend that the system isn't
   // running on battery.
-  power_metrics_reporter_->battery_state_for_testing().on_battery = false;
+  power_metrics_reporter_->battery_state_for_testing()
+      ->is_external_power_connected = true;
 
   process_monitor_.SetMetricsToReturn({});
 
   // Push a battery state that indicates that the system is still not running
   // on battery.
   battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 1.0, /* on_battery - */ false, base::TimeTicks::Now()});
+      .battery_count = 1,
+      .is_external_power_connected = true,
+      .current_capacity = 50,
+      .full_charged_capacity = 100,
+      .capture_time = base::TimeTicks::Now()});
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.source_id_for_longest_visible_origin =
@@ -528,7 +536,11 @@
   // The initial battery state indicates that the system is running on battery,
   // pretends that this has changed.
   battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 1.0, /* on_battery - */ false, base::TimeTicks::Now()});
+      .battery_count = 1,
+      .is_external_power_connected = true,
+      .current_capacity = 100,
+      .full_charged_capacity = 100,
+      .capture_time = base::TimeTicks::Now()});
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.source_id_for_longest_visible_origin =
@@ -555,8 +567,7 @@
   process_monitor_.SetMetricsToReturn({});
 
   // A nullopt battery value indicates that the battery level is unavailable.
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, absl::nullopt, true, base::TimeTicks::Now()});
+  battery_states_.push(absl::nullopt);
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.source_id_for_longest_visible_origin =
@@ -572,20 +583,28 @@
       entries[0], UkmEntry::kBatteryDischargeRateName));
   test_ukm_recorder_.ExpectEntryMetric(
       entries[0], UkmEntry::kBatteryDischargeModeName,
-      static_cast<int64_t>(BatteryDischargeMode::kChargeLevelUnavailable));
+      static_cast<int64_t>(BatteryDischargeMode::kRetrievalError));
 
   histogram_tester_.ExpectTotalCount(kBatteryDischargeRateHistogramName, 0);
-  histogram_tester_.ExpectUniqueSample(
-      kBatteryDischargeModeHistogramName,
-      BatteryDischargeMode::kChargeLevelUnavailable, 1);
+  histogram_tester_.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
+                                       BatteryDischargeMode::kRetrievalError,
+                                       1);
 }
 
 TEST_F(PowerMetricsReporterUnitTest, UKMsNoBattery) {
+  power_metrics_reporter_->battery_state_for_testing()->battery_count = 0;
+  power_metrics_reporter_->battery_state_for_testing()
+      ->is_external_power_connected = true;
+
   process_monitor_.SetMetricsToReturn({});
 
   // Indicates that the system has no battery interface.
   battery_states_.push(BatteryLevelProvider::BatteryState{
-      0, 0, 1.0, false, base::TimeTicks::Now()});
+      .battery_count = 0,
+      .is_external_power_connected = true,
+      .current_capacity = absl::nullopt,
+      .full_charged_capacity = absl::nullopt,
+      .capture_time = base::TimeTicks::Now()});
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.source_id_for_longest_visible_origin =
@@ -613,12 +632,10 @@
 // in a kDischarging value emitted. See https://crbug.com/1249830.
 TEST_F(PowerMetricsReporterUnitTest, UKMsMacFullyCharged) {
   // Set the initial battery level at 100%.
-  power_metrics_reporter_->battery_state_for_testing().charge_level = 1.0;
+  power_metrics_reporter_->battery_state_for_testing()->current_capacity = 100;
 
   process_monitor_.SetMetricsToReturn({});
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      0, 1, 1.0, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(100));
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.source_id_for_longest_visible_origin =
@@ -644,14 +661,10 @@
 #endif  // BUILDFLAG(IS_MAC)
 
 TEST_F(PowerMetricsReporterUnitTest, UKMsBatteryStateIncrease) {
-  // Set the initial battery level at 50%.
-  power_metrics_reporter_->battery_state_for_testing().charge_level = 0.5;
-
   process_monitor_.SetMetricsToReturn({});
 
   // Set the new battery state at 75%.
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.75, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(75));
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.source_id_for_longest_visible_origin =
@@ -678,9 +691,7 @@
 
 TEST_F(PowerMetricsReporterUnitTest, UKMsNoTab) {
   process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.50, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(50));
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.max_tab_count = 0;
@@ -704,9 +715,7 @@
 
 TEST_F(PowerMetricsReporterUnitTest, DurationsLongerThanIntervalAreCapped) {
   process_monitor_.SetMetricsToReturn(GetFakeProcessMetrics());
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.50, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(50));
 
   UsageScenarioDataStore::IntervalData fake_interval_data;
   fake_interval_data.time_playing_video_full_screen_single_monitor =
@@ -730,9 +739,7 @@
 
 TEST_F(PowerMetricsReporterUnitTest, UKMsWithSleepEvent) {
   process_monitor_.SetMetricsToReturn({});
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.50, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(50));
 
   UsageScenarioDataStore::IntervalData fake_interval_data = {};
   fake_interval_data.sleep_events = 1;
@@ -754,9 +761,7 @@
 // advances and resource coalition data is available.
 TEST_F(PowerMetricsReporterUnitTest, ShortIntervalHistograms_EndToEnd) {
   process_monitor_.SetMetricsToReturn({});
-
-  battery_states_.push(BatteryLevelProvider::BatteryState{
-      1, 1, 0.30, true, base::TimeTicks::Now()});
+  battery_states_.push(MakeBatteryDischarginState(30));
 
   UsageScenarioDataStore::IntervalData interval_data;
   interval_data.max_tab_count = 1;
diff --git a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc
new file mode 100644
index 0000000..903fdd4
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.cc
@@ -0,0 +1,102 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h"
+
+#include <string>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "chrome/browser/browser_process.h"
+#include "components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h"
+#include "components/page_load_metrics/browser/page_load_metrics_util.h"
+#include "components/page_load_metrics/common/page_load_timing.h"
+#include "content/public/browser/navigation_handle.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+
+using page_load_metrics::PageAbortReason;
+
+namespace internal {
+
+const char kHistogramGWSFirstContentfulPaint[] =
+    "PageLoad.Clients.GoogleSearch.PaintTiming."
+    "NavigationToFirstContentfulPaint";
+const char kHistogramGWSLargestContentfulPaint[] =
+    "PageLoad.Clients.GoogleSearch.PaintTiming."
+    "NavigationToLargestContentfulPaint";
+const char kHistogramGWSParseStart[] =
+    "PageLoad.Clients.GoogleSearch.ParseTiming.NavigationToParseStart";
+
+}  // namespace internal
+
+GWSPageLoadMetricsObserver::GWSPageLoadMetricsObserver() = default;
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+GWSPageLoadMetricsObserver::OnCommit(
+    content::NavigationHandle* navigation_handle) {
+  return page_load_metrics::IsGoogleSearchResultUrl(navigation_handle->GetURL())
+             ? CONTINUE_OBSERVING
+             : STOP_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+GWSPageLoadMetricsObserver::OnFencedFramesStart(
+    content::NavigationHandle* navigation_handle,
+    const GURL& currently_committed_url) {
+  // This class is interested only in events that are preprocessed and
+  // dispatched also to the outermost page at PageLoadTracker. So, this class
+  // doesn't need to forward events for FencedFrames.
+  return STOP_OBSERVING;
+}
+
+void GWSPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_contentful_paint, GetDelegate())) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramGWSFirstContentfulPaint,
+                      timing.paint_timing->first_contentful_paint.value());
+}
+
+void GWSPageLoadMetricsObserver::OnParseStart(
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
+          timing.parse_timing->parse_start, GetDelegate())) {
+    return;
+  }
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramGWSParseStart,
+                      timing.parse_timing->parse_start.value());
+}
+
+void GWSPageLoadMetricsObserver::OnComplete(
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  LogMetricsOnComplete();
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+GWSPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  LogMetricsOnComplete();
+  return STOP_OBSERVING;
+}
+
+void GWSPageLoadMetricsObserver::LogMetricsOnComplete() {
+  const page_load_metrics::ContentfulPaintTimingInfo&
+      all_frames_largest_contentful_paint =
+          GetDelegate()
+              .GetLargestContentfulPaintHandler()
+              .MergeMainFrameAndSubframes();
+  if (!all_frames_largest_contentful_paint.ContainsValidTime() ||
+      !WasStartedInForegroundOptionalEventInForeground(
+          all_frames_largest_contentful_paint.Time(), GetDelegate())) {
+    return;
+  }
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramGWSLargestContentfulPaint,
+                      all_frames_largest_contentful_paint.Time().value());
+}
diff --git a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h
new file mode 100644
index 0000000..5fc65d15
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_GWS_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_GWS_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include "components/google/core/common/google_util.h"
+#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+
+namespace internal {
+// Exposed for tests.
+extern const char kHistogramGWSFirstContentfulPaint[];
+extern const char kHistogramGWSLargestContentfulPaint[];
+extern const char kHistogramGWSParseStart[];
+
+}  // namespace internal
+
+class GWSPageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+  GWSPageLoadMetricsObserver();
+
+  GWSPageLoadMetricsObserver(const GWSPageLoadMetricsObserver&) = delete;
+  GWSPageLoadMetricsObserver& operator=(const GWSPageLoadMetricsObserver&) =
+      delete;
+
+  // page_load_metrics::PageLoadMetricsObserver implementation:
+  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle) override;
+
+  ObservePolicy OnFencedFramesStart(
+      content::NavigationHandle* navigation_handle,
+      const GURL& currently_committed_url) override;
+
+  ObservePolicy FlushMetricsOnAppEnterBackground(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+
+  void OnFirstContentfulPaintInPage(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnParseStart(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+  void OnComplete(
+      const page_load_metrics::mojom::PageLoadTiming& timing) override;
+
+ private:
+  void LogMetricsOnComplete();
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_FROM_GWS_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer_unittest.cc
new file mode 100644
index 0000000..edfb591
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h"
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/page_load_metrics_util.h"
+#include "components/page_load_metrics/browser/page_load_tracker.h"
+#include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
+#include "content/public/browser/web_contents.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
+
+namespace {
+
+constexpr char kGoogleSearchResultsUrl[] = "https://www.google.com/search?q=d";
+
+}  // namespace
+
+class GWSPageLoadMetricsObserverTest
+    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+ public:
+  // page_load_metrics::PageLoadMetricsObserverTestHarness:
+  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
+    auto observer = std::make_unique<GWSPageLoadMetricsObserver>();
+    observer_ = observer.get();
+    tracker->AddObserver(std::move(observer));
+  }
+
+  void SimulateTimingWithoutPaint() {
+    page_load_metrics::mojom::PageLoadTiming timing;
+    page_load_metrics::InitPageLoadTimingForTest(&timing);
+    timing.navigation_start = base::Time::FromDoubleT(1);
+    tester()->SimulateTimingUpdate(timing);
+  }
+
+  void SimulateTimingWithFirstPaint() {
+    page_load_metrics::mojom::PageLoadTiming timing;
+    page_load_metrics::InitPageLoadTimingForTest(&timing);
+    timing.parse_timing->parse_start = base::Milliseconds(0);
+    timing.navigation_start = base::Time::FromDoubleT(1);
+    timing.paint_timing->first_paint = base::Milliseconds(0);
+    PopulateRequiredTimingFields(&timing);
+    tester()->SimulateTimingUpdate(timing);
+  }
+
+ protected:
+  raw_ptr<GWSPageLoadMetricsObserver> observer_ = nullptr;
+};
+
+class GWSPageLoadMetricsLoggerTest : public testing::Test {};
+
+TEST_F(GWSPageLoadMetricsObserverTest, Search) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  timing.parse_timing->parse_start = base::Milliseconds(1);
+  timing.paint_timing->first_contentful_paint = base::Milliseconds(10);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint =
+      base::Milliseconds(100);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 20u;
+  PopulateRequiredTimingFields(&timing);
+  NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
+
+  tester()->SimulateTimingUpdate(timing);
+
+  // Navigate again to force logging.
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSParseStart, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramGWSParseStart, 1, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSFirstContentfulPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramGWSFirstContentfulPaint, 10, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSLargestContentfulPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramGWSLargestContentfulPaint, 100, 1);
+}
+
+TEST_F(GWSPageLoadMetricsObserverTest, NonSearch) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  timing.parse_timing->parse_start = base::Milliseconds(1);
+  timing.paint_timing->first_contentful_paint = base::Milliseconds(10);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint =
+      base::Milliseconds(100);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 20u;
+  PopulateRequiredTimingFields(&timing);
+  NavigateAndCommit(GURL("https://www.google.com/foo&q=test"));
+
+  tester()->SimulateTimingUpdate(timing);
+  // Navigate again to force logging.
+  tester()->NavigateToUntrackedUrl();
+
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSParseStart, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSFirstContentfulPaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSLargestContentfulPaint, 0);
+}
+
+TEST_F(GWSPageLoadMetricsObserverTest, SearchBackground) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.parse_timing->parse_start = base::Seconds(60);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  timing.paint_timing->first_contentful_paint = base::Seconds(60);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint =
+      base::Seconds(60);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 20u;
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
+  web_contents()->WasHidden();
+  tester()->SimulateTimingUpdate(timing);
+  // Navigate again to force logging.
+  tester()->NavigateToUntrackedUrl();
+
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSParseStart, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSFirstContentfulPaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSLargestContentfulPaint, 0);
+}
+
+TEST_F(GWSPageLoadMetricsObserverTest, SearchBackgroundLater) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.parse_timing->parse_start = base::Microseconds(1);
+  timing.navigation_start = base::Time::FromDoubleT(1);
+  timing.paint_timing->first_contentful_paint = base::Microseconds(1);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint =
+      base::Microseconds(1);
+  timing.paint_timing->largest_contentful_paint->largest_text_paint_size = 20u;
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
+  // Sleep to make sure the backgrounded time is > than the paint time, even
+  // for low resolution timers.
+  base::PlatformThread::Sleep(base::Milliseconds(50));
+  web_contents()->WasHidden();
+  tester()->SimulateTimingUpdate(timing);
+  // Navigate again to force logging.
+  tester()->NavigateToUntrackedUrl();
+
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSParseStart, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramGWSParseStart, 0, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSFirstContentfulPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramGWSFirstContentfulPaint, 0, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramGWSLargestContentfulPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramGWSLargestContentfulPaint, 0, 1);
+}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 61750ee..8fba39c 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h"
 #include "chrome/browser/page_load_metrics/observers/formfill_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/gws_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/javascript_frameworks_ukm_observer.h"
 #include "chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h"
@@ -127,6 +128,7 @@
     tracker->AddObserver(std::make_unique<JavascriptFrameworksUkmObserver>());
     tracker->AddObserver(std::make_unique<SchemePageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<FromGWSPageLoadMetricsObserver>());
+    tracker->AddObserver(std::make_unique<GWSPageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<ForegroundDurationUKMObserver>());
     tracker->AddObserver(
         std::make_unique<DocumentWritePageLoadMetricsObserver>());
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 5ec2b7b..61cf7f7 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -173,6 +173,7 @@
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerAndroidBackendUtilTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerLifecycleHelperTest.java",
+    "junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactoryTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridgeTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeTest.java",
     "junit/src/org/chromium/chrome/browser/password_manager/PasswordSyncControllerDelegateBridgeTest.java",
@@ -210,6 +211,7 @@
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_test_core_java",
+    "//third_party/hamcrest:hamcrest_library_java",
     "//third_party/junit",
     "//ui/android:ui_no_recycler_view_java",
   ]
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactory.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactory.java
index d361e269..d89552e 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactory.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactory.java
@@ -4,11 +4,33 @@
 
 package org.chromium.chrome.browser.password_manager;
 
+import static org.chromium.base.ThreadUtils.assertOnUiThread;
+
+import androidx.annotation.VisibleForTesting;
+
 /**
  * This factory returns an implementation for the password settings accessor. The factory itself is
  * also implemented downstream.
  */
 public abstract class PasswordSettingsAccessorFactory {
+    private static PasswordSettingsAccessorFactory sInstance;
+
+    protected PasswordSettingsAccessorFactory() {}
+
+    /**
+     * Returns an accessor factory to be invoked whenever {@link #createAccessor()} is called. If no
+     * factory was used yet, it is created.
+     *
+     * @return The shared {@link PasswordSettingsAccessorFactory} instance.
+     */
+    public static PasswordSettingsAccessorFactory getOrCreate() {
+        assertOnUiThread();
+        if (sInstance == null) {
+            sInstance = new PasswordSettingsAccessorFactoryImpl();
+        }
+        return sInstance;
+    }
+
     /**
      * Returns the downstream implementation provided by subclasses.
      *
@@ -21,4 +43,9 @@
     public boolean canCreateAccessor() {
         return false;
     }
+
+    @VisibleForTesting
+    public static void setupFactoryForTesting(PasswordSettingsAccessorFactory accessorFactory) {
+        sInstance = accessorFactory;
+    }
 }
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridge.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridge.java
index cfe486976c..eb7b43e1 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridge.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridge.java
@@ -34,12 +34,12 @@
     @CalledByNative
     static PasswordSettingsUpdaterBridge create(long nativeSettingsUpdaterBridge) {
         return new PasswordSettingsUpdaterBridge(nativeSettingsUpdaterBridge,
-                new PasswordSettingsAccessorFactoryImpl().createAccessor());
+                PasswordSettingsAccessorFactoryImpl.getOrCreate().createAccessor());
     }
 
     @CalledByNative
     static boolean canCreateAccessor() {
-        return new PasswordSettingsAccessorFactoryImpl().canCreateAccessor();
+        return PasswordSettingsAccessorFactoryImpl.getOrCreate().canCreateAccessor();
     }
 
     @CalledByNative
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactoryTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactoryTest.java
new file mode 100644
index 0000000..6b7b06e
--- /dev/null
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsAccessorFactoryTest.java
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Tests for the methods of {@link PasswordSettingsAccessorFactory}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class PasswordSettingsAccessorFactoryTest {
+    @Test
+    public void testGetOrCreateReusesExistingFactory() {
+        PasswordSettingsAccessorFactory firstFactoryInstance =
+                PasswordSettingsAccessorFactory.getOrCreate();
+        PasswordSettingsAccessorFactory secondFactoryInstance =
+                PasswordSettingsAccessorFactory.getOrCreate();
+        assertThat(firstFactoryInstance, sameInstance(secondFactoryInstance));
+    }
+
+    @Test
+    public void testSetupFactoryForTestingUsesTheTestingFactory() {
+        PasswordSettingsAccessorFactory passwordSettingsAccessorFactory =
+                mock(PasswordSettingsAccessorFactory.class);
+        PasswordSettingsAccessorFactory.setupFactoryForTesting(passwordSettingsAccessorFactory);
+        assertEquals(
+                PasswordSettingsAccessorFactory.getOrCreate(), passwordSettingsAccessorFactory);
+    }
+}
diff --git a/chrome/browser/policy/test/hsts_policy_browsertest.cc b/chrome/browser/policy/test/hsts_policy_browsertest.cc
index 260ecd06..f1b88c4 100644
--- a/chrome/browser/policy/test/hsts_policy_browsertest.cc
+++ b/chrome/browser/policy/test/hsts_policy_browsertest.cc
@@ -26,10 +26,10 @@
   void SetUpInProcessBrowserTestFixture() override {
     PolicyTest::SetUpInProcessBrowserTestFixture();
     PolicyMap policies;
-    std::vector<base::Value> bypass_list;
-    bypass_list.emplace_back(base::Value("example"));
+    base::Value::List bypass_list;
+    bypass_list.Append("example");
     SetPolicy(&policies, key::kHSTSPolicyBypassList,
-              base::ListValue(bypass_list));
+              base::Value(std::move(bypass_list)));
     provider_.UpdateChromePolicy(policies);
   }
 };
diff --git a/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc b/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc
index fc734389..345ee51 100644
--- a/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc
+++ b/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc
@@ -86,8 +86,8 @@
 
   // Add a policy to allow overriding on specific sites only. Since
   // kSSLErrorOverrideAllowed is enabled, this should do nothing.
-  std::vector<base::Value> allow_list;
-  allow_list.emplace_back(base::Value("example.com"));
+  base::Value::List allow_list;
+  allow_list.Append("example.com");
   PolicyMap policies;
   policies.Set(key::kSSLErrorOverrideAllowedForOrigins, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
@@ -175,8 +175,8 @@
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(false),
                nullptr);
   // Add a policy to allow overriding on specific sites only.
-  std::vector<base::Value> allow_list;
-  allow_list.emplace_back(base::Value("example.com"));
+  base::Value::List allow_list;
+  allow_list.Append("example.com");
   policies.Set(key::kSSLErrorOverrideAllowedForOrigins, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                base::Value(std::move(allow_list)), nullptr);
@@ -225,10 +225,10 @@
   policies.Set(key::kSSLErrorOverrideAllowed, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(false),
                nullptr);
-  std::vector<base::Value> allow_list;
+  base::Value::List allow_list;
   // We ignore "*" or badly formed patterns as inputs.
-  allow_list.emplace_back(base::Value("*"));
-  allow_list.emplace_back(base::Value("bad 127.0.0.1 input"));
+  allow_list.Append("*");
+  allow_list.Append("bad 127.0.0.1 input");
   policies.Set(key::kSSLErrorOverrideAllowedForOrigins, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                base::Value(std::move(allow_list)), nullptr);
@@ -277,7 +277,7 @@
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(false),
                nullptr);
   // The policy is intentionally configured with an empty list for this test.
-  std::vector<base::Value> allow_list;
+  base::Value::List allow_list;
   policies.Set(key::kSSLErrorOverrideAllowedForOrigins, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                base::Value(std::move(allow_list)), nullptr);
@@ -331,8 +331,8 @@
                nullptr);
   // Add a policy to allow overriding on specific sites only. The path should be
   // ignored.
-  std::vector<base::Value> allow_list;
-  allow_list.emplace_back(base::Value("127.0.0.1/my/path/to/file.ext"));
+  base::Value::List allow_list;
+  allow_list.Append("127.0.0.1/my/path/to/file.ext");
   policies.Set(key::kSSLErrorOverrideAllowedForOrigins, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                base::Value(std::move(allow_list)), nullptr);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
index 31f7ea1..0b27936 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 
 /** Class that implements a macro that deletes the previous sentence. */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/dictation_macros_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/dictation_macros_test.js
index 2d0fd67..2bbf4a8 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/dictation_macros_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/dictation_macros_test.js
@@ -48,7 +48,8 @@
 
 AX_TEST_F('DictationMacrosTest', 'RepeatableKeyPressMacro', async function() {
   // DELETE_PREV_CHAR is one of many RepeatableKeyPressMacros.
-  const macro = await this.getSimpleParseStrategy().parse('delete');
+  const macro = await this.getSimpleParseStrategy().parse(
+      'delete the previous character');
   assertEquals('DELETE_PREV_CHAR', macro.getMacroNameString());
   const checkContextResult = macro.checkContext();
   assertTrue(checkContextResult.canTryAction);
@@ -88,3 +89,25 @@
   assertFalse(this.getDictationActive());
   assertFalse(this.getSpeechRecognitionActive());
 });
+
+// Tests that smart macros can be parsed and constructed with the correct
+// arguments.
+SYNC_TEST_F('DictationMacrosTest', 'SmartMacros', async function() {
+  const strategy = this.getSimpleParseStrategy();
+  assertNotNullNorUndefined(strategy);
+  let macro = await strategy.parse('delete hello world');
+  assertEquals('SMART_DELETE_PHRASE', macro.getMacroNameString());
+  assertEquals('hello world', macro.phrase_);
+  macro = await strategy.parse('replace hello with goodbye');
+  assertEquals('SMART_REPLACE_PHRASE', macro.getMacroNameString());
+  assertEquals('hello', macro.deletePhrase_);
+  assertEquals('goodbye', macro.insertPhrase_);
+  macro = await strategy.parse('insert hello before goodbye');
+  assertEquals('SMART_INSERT_BEFORE', macro.getMacroNameString());
+  assertEquals('hello', macro.insertPhrase_);
+  assertEquals('goodbye', macro.beforePhrase_);
+  macro = await strategy.parse('select from hello to goodbye');
+  assertEquals('SMART_SELECT_BTWN_INCL', macro.getMacroNameString());
+  assertEquals('hello', macro.startPhrase_);
+  assertEquals('goodbye', macro.endPhrase_);
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
index c3229b5..e1f16706 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
@@ -119,16 +119,4 @@
  * @const {!Array<!MacroName>}
  * @private
  */
-HiddenMacroManager.HIDDEN_MACROS_ = [
-  MacroName.STOP_LISTENING,
-  MacroName.DELETE_PREV_WORD,
-  MacroName.DELETE_PREV_SENT,
-  MacroName.NAV_NEXT_WORD,
-  MacroName.NAV_PREV_WORD,
-  MacroName.SMART_DELETE_PHRASE,
-  MacroName.SMART_REPLACE_PHRASE,
-  MacroName.SMART_INSERT_BEFORE,
-  MacroName.SMART_SELECT_BTWN_INCL,
-  MacroName.NAV_NEXT_SENT,
-  MacroName.NAV_PREV_SENT,
-];
+HiddenMacroManager.HIDDEN_MACROS_ = [];
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
index 197bb2fd..c747b5ce 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 
 /** Implements a macro that moves the text caret to the next sentence. */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js
index e15e3b4..810c71a69 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 
 /** Implements a macro that deletes a provided word or phrase. */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js
index 11e954b..8b52766 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js
index d03dd70e..bc0075f 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js
index ead1cb5..b2ad72c 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 
 /** Implements a macro that sets selection between two words or phrases. */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js
index 68c397f..614738c 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_parse_test.js
@@ -27,7 +27,9 @@
   assertNotNullNorUndefined(strategy);
   let macro = await strategy.parse('Hello world');
   assertEquals('INPUT_TEXT_VIEW', macro.getMacroNameString());
-  macro = await strategy.parse('delete');
+  macro = await strategy.parse('type delete the previous character');
+  assertEquals('INPUT_TEXT_VIEW', macro.getMacroNameString());
+  macro = await strategy.parse('delete the previous character');
   assertEquals('DELETE_PREV_CHAR', macro.getMacroNameString());
   macro = await strategy.parse('move to the previous character');
   assertEquals('NAV_PREV_CHAR', macro.getMacroNameString());
@@ -55,6 +57,28 @@
   assertEquals('LIST_COMMANDS', macro.getMacroNameString());
   macro = await strategy.parse('new line');
   assertEquals('NEW_LINE', macro.getMacroNameString());
+  macro = await strategy.parse('cancel');
+  assertEquals('STOP_LISTENING', macro.getMacroNameString());
+  macro = await strategy.parse('delete the previous word');
+  assertEquals('DELETE_PREV_WORD', macro.getMacroNameString());
+  macro = await strategy.parse('delete the previous sentence');
+  assertEquals('DELETE_PREV_SENT', macro.getMacroNameString());
+  macro = await strategy.parse('move to the next word');
+  assertEquals('NAV_NEXT_WORD', macro.getMacroNameString());
+  macro = await strategy.parse('move to the previous word');
+  assertEquals('NAV_PREV_WORD', macro.getMacroNameString());
+  macro = await strategy.parse('delete hello world');
+  assertEquals('SMART_DELETE_PHRASE', macro.getMacroNameString());
+  macro = await strategy.parse('replace hello world with goodnight world');
+  assertEquals('SMART_REPLACE_PHRASE', macro.getMacroNameString());
+  macro = await strategy.parse('insert hello world before goodnight world');
+  assertEquals('SMART_INSERT_BEFORE', macro.getMacroNameString());
+  macro = await strategy.parse('select from hello world to goodnight world');
+  assertEquals('SMART_SELECT_BTWN_INCL', macro.getMacroNameString());
+  macro = await strategy.parse('move to the next sentence');
+  assertEquals('NAV_NEXT_SENT', macro.getMacroNameString());
+  macro = await strategy.parse('move to the previous sentence');
+  assertEquals('NAV_PREV_SENT', macro.getMacroNameString());
 });
 
 // TODO(crbug.com/1264544): This test fails because of a memory issues
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
index 0b1dcf7..920b067f 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
@@ -8,18 +8,25 @@
  */
 
 import {InputController} from '/accessibility_common/dictation/input_controller.js';
+import {DeletePrevSentMacro} from '/accessibility_common/dictation/macros/delete_prev_sent_macro.js';
 import {HiddenMacroManager} from '/accessibility_common/dictation/macros/hidden_macro_manager.js';
 import {InputTextViewMacro, NewLineMacro} from '/accessibility_common/dictation/macros/input_text_view_macro.js';
 import {ListCommandsMacro} from '/accessibility_common/dictation/macros/list_commands_macro.js';
 import {Macro} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
-import * as RepeatableKeyPressMacro from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
+import {NavNextSentMacro, NavPrevSentMacro} from '/accessibility_common/dictation/macros/nav_sent_macro.js';
+import * as RepeatableKeyPress from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
+import {SmartDeletePhraseMacro} from '/accessibility_common/dictation/macros/smart_delete_phrase_macro.js';
+import {SmartInsertBeforeMacro} from '/accessibility_common/dictation/macros/smart_insert_before_macro.js';
+import {SmartReplacePhraseMacro} from '/accessibility_common/dictation/macros/smart_replace_phrase_macro.js';
+import {SmartSelectBetweenMacro} from '/accessibility_common/dictation/macros/smart_select_between_macro.js';
+import {StopListeningMacro} from '/accessibility_common/dictation/macros/stop_listening_macro.js';
 import {ParseStrategy} from '/accessibility_common/dictation/parse/parse_strategy.js';
 
 /**
  * @typedef {{
  *   messageId: string,
- *   build: Function,
+ *   build: function(*): !Macro,
  * }}
  */
 let MacroData;
@@ -34,6 +41,11 @@
    * @param {boolean} isRTLLocale
    */
   constructor(macroName, inputController, isRTLLocale) {
+    if (!SimpleMacroFactory.getData_()[macroName]) {
+      throw new Error(
+          'Macro is not supported by SimpleMacroFactory: ' + macroName);
+    }
+
     /** @private {!MacroName} */
     this.macroName_ = macroName;
     /** @private {!InputController} */
@@ -41,45 +53,87 @@
     /** @private {boolean} */
     this.isRTLLocale_ = isRTLLocale;
 
-    if (!SimpleMacroFactory.getData_()[this.macroName_]) {
-      throw new Error(
-          'Macro is not supported by SimpleMacroFactory: ' + this.macroName_);
-    }
-
-    /** @private {string} */
-    this.commandString_ = chrome.i18n.getMessage(
-        SimpleMacroFactory.getData_()[this.macroName_].messageId);
+    /** @private {RegExp} */
+    this.commandRegex_ = null;
+    this.initializeCommandRegex_(this.macroName_);
   }
 
-  /** @return {Macro} */
-  createMacro() {
+  /**
+   * Builds a RegExp that can be used to parse a command. For example, the
+   * SmartReplacePhraseMacro can be parsed with the pattern:
+   * /replace (.*) with (.*)/i.
+   * @param {!MacroName} macroName
+   * @private
+   */
+  initializeCommandRegex_(macroName) {
+    const matchAnythingPattern = '(.*)';
     const args = [];
+    switch (macroName) {
+      case MacroName.SMART_DELETE_PHRASE:
+        args.push(matchAnythingPattern);
+        break;
+      case MacroName.SMART_REPLACE_PHRASE:
+      case MacroName.SMART_INSERT_BEFORE:
+      case MacroName.SMART_SELECT_BTWN_INCL:
+        args.push(matchAnythingPattern, matchAnythingPattern);
+        break;
+    }
+    const message = chrome.i18n.getMessage(
+        SimpleMacroFactory.getData_()[macroName].messageId, args);
+    const pattern = `^${message}`;
+    this.commandRegex_ = new RegExp(pattern, 'i');
+  }
+
+  /**
+   * @param {string} text
+   * @return {Macro|null}
+   */
+  createMacro(text) {
+    // Check whether `text` matches `this.commandRegex_`, ignoring case and
+    // whitespace.
+    text = text.trim().toLowerCase();
+    if (!this.commandRegex_.test(text)) {
+      return null;
+    }
+
+    const initialArgs = [];
     switch (this.macroName_) {
       case MacroName.NAV_PREV_CHAR:
       case MacroName.NAV_NEXT_CHAR:
       case MacroName.UNSELECT_TEXT:
-        args.push(this.isRTLLocale_);
+      case MacroName.NAV_NEXT_WORD:
+      case MacroName.NAV_PREV_WORD:
+        initialArgs.push(this.isRTLLocale_);
         break;
       case MacroName.NEW_LINE:
-        args.push(this.inputController_);
+      case MacroName.DELETE_PREV_SENT:
+      case MacroName.SMART_DELETE_PHRASE:
+      case MacroName.SMART_REPLACE_PHRASE:
+      case MacroName.SMART_INSERT_BEFORE:
+      case MacroName.SMART_SELECT_BTWN_INCL:
+        initialArgs.push(this.inputController_);
+        break;
+      case MacroName.NAV_NEXT_SENT:
+      case MacroName.NAV_PREV_SENT:
+        initialArgs.push(this.inputController_, this.isRTLLocale_);
         break;
     }
 
+    const result = this.commandRegex_.exec(text);
+    // `result[0]` contains the entire matched text, while all subsequent
+    // indices contain text matched by each /(.*)/. We're only interested in
+    // text matched by
+    // /(.*)/, so ignore `result[0]`.
+    const extractedArgs = result.slice(1);
+    const finalArgs = initialArgs.concat(extractedArgs);
     const data = SimpleMacroFactory.getData_();
-    return new data[this.macroName_].build(...args);
+    return new data[this.macroName_].build(...finalArgs);
   }
 
   /**
-   * Checks whether a string matches `commandString_`, ignoring case and
-   * whitespace.
-   * @param {string} text
-   * @return {boolean}
-   */
-  matchesMacro(text) {
-    return text.trim().toLowerCase() === this.commandString_;
-  }
-
-  /**
+   * Returns data that is used to create a macro. `messageId` is used to
+   * retrieve the macro's command string and `build` is used to construct the
+   * macro.
    * @return {Object<MacroName, MacroData>}
    * @private
    */
@@ -87,51 +141,51 @@
     return {
       [MacroName.DELETE_PREV_CHAR]: {
         messageId: 'dictation_command_delete_prev_char',
-        build: RepeatableKeyPressMacro.DeletePreviousCharacterMacro
+        build: RepeatableKeyPress.DeletePreviousCharacterMacro
       },
       [MacroName.NAV_PREV_CHAR]: {
         messageId: 'dictation_command_nav_prev_char',
-        build: RepeatableKeyPressMacro.NavPreviousCharMacro
+        build: RepeatableKeyPress.NavPreviousCharMacro
       },
       [MacroName.NAV_NEXT_CHAR]: {
         messageId: 'dictation_command_nav_next_char',
-        build: RepeatableKeyPressMacro.NavNextCharMacro
+        build: RepeatableKeyPress.NavNextCharMacro
       },
       [MacroName.NAV_PREV_LINE]: {
         messageId: 'dictation_command_nav_prev_line',
-        build: RepeatableKeyPressMacro.NavPreviousLineMacro
+        build: RepeatableKeyPress.NavPreviousLineMacro
       },
       [MacroName.NAV_NEXT_LINE]: {
         messageId: 'dictation_command_nav_next_line',
-        build: RepeatableKeyPressMacro.NavNextLineMacro
+        build: RepeatableKeyPress.NavNextLineMacro
       },
       [MacroName.COPY_SELECTED_TEXT]: {
         messageId: 'dictation_command_copy_selected_text',
-        build: RepeatableKeyPressMacro.CopySelectedTextMacro
+        build: RepeatableKeyPress.CopySelectedTextMacro
       },
       [MacroName.PASTE_TEXT]: {
         messageId: 'dictation_command_paste_text',
-        build: RepeatableKeyPressMacro.PasteTextMacro
+        build: RepeatableKeyPress.PasteTextMacro
       },
       [MacroName.CUT_SELECTED_TEXT]: {
         messageId: 'dictation_command_cut_selected_text',
-        build: RepeatableKeyPressMacro.CutSelectedTextMacro
+        build: RepeatableKeyPress.CutSelectedTextMacro
       },
       [MacroName.UNDO_TEXT_EDIT]: {
         messageId: 'dictation_command_undo_text_edit',
-        build: RepeatableKeyPressMacro.UndoTextEditMacro
+        build: RepeatableKeyPress.UndoTextEditMacro
       },
       [MacroName.REDO_ACTION]: {
         messageId: 'dictation_command_redo_action',
-        build: RepeatableKeyPressMacro.RedoActionMacro
+        build: RepeatableKeyPress.RedoActionMacro
       },
       [MacroName.SELECT_ALL_TEXT]: {
         messageId: 'dictation_command_select_all_text',
-        build: RepeatableKeyPressMacro.SelectAllTextMacro
+        build: RepeatableKeyPress.SelectAllTextMacro
       },
       [MacroName.UNSELECT_TEXT]: {
         messageId: 'dictation_command_unselect_text',
-        build: RepeatableKeyPressMacro.UnselectTextMacro
+        build: RepeatableKeyPress.UnselectTextMacro
       },
       [MacroName.LIST_COMMANDS]: {
         messageId: 'dictation_command_list_commands',
@@ -139,6 +193,50 @@
       },
       [MacroName.NEW_LINE]:
           {messageId: 'dictation_command_new_line', build: NewLineMacro},
+      [MacroName.STOP_LISTENING]: {
+        messageId: 'dictation_command_stop_listening',
+        build: StopListeningMacro
+      },
+      [MacroName.DELETE_PREV_WORD]: {
+        messageId: 'dictation_command_delete_prev_word',
+        build: RepeatableKeyPress.DeletePrevWordMacro
+      },
+      [MacroName.DELETE_PREV_SENT]: {
+        messageId: 'dictation_command_delete_prev_sent',
+        build: DeletePrevSentMacro
+      },
+      [MacroName.NAV_NEXT_WORD]: {
+        messageId: 'dictation_command_nav_next_word',
+        build: RepeatableKeyPress.NavNextWordMacro
+      },
+      [MacroName.NAV_PREV_WORD]: {
+        messageId: 'dictation_command_nav_prev_word',
+        build: RepeatableKeyPress.NavPrevWordMacro
+      },
+      [MacroName.SMART_DELETE_PHRASE]: {
+        messageId: 'dictation_command_smart_delete_phrase',
+        build: SmartDeletePhraseMacro
+      },
+      [MacroName.SMART_REPLACE_PHRASE]: {
+        messageId: 'dictation_command_smart_replace_phrase',
+        build: SmartReplacePhraseMacro
+      },
+      [MacroName.SMART_INSERT_BEFORE]: {
+        messageId: 'dictation_command_smart_insert_before',
+        build: SmartInsertBeforeMacro
+      },
+      [MacroName.SMART_SELECT_BTWN_INCL]: {
+        messageId: 'dictation_command_smart_select_btwn_incl',
+        build: SmartSelectBetweenMacro
+      },
+      [MacroName.NAV_NEXT_SENT]: {
+        messageId: 'dictation_command_nav_next_sent',
+        build: NavNextSentMacro
+      },
+      [MacroName.NAV_PREV_SENT]: {
+        messageId: 'dictation_command_nav_prev_sent',
+        build: NavPrevSentMacro
+      },
     };
   }
 }
@@ -181,8 +279,9 @@
   /** @override */
   async parse(text) {
     for (const [name, factory] of this.macroFactoryMap_) {
-      if (factory.matchesMacro(text)) {
-        return factory.createMacro();
+      const macro = factory.createMacro(text);
+      if (macro) {
+        return macro;
       }
     }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/dictation_strings.grdp b/chrome/browser/resources/chromeos/accessibility/strings/dictation_strings.grdp
index 5a47a2fd8..107e86e97 100644
--- a/chrome/browser/resources/chromeos/accessibility/strings/dictation_strings.grdp
+++ b/chrome/browser/resources/chromeos/accessibility/strings/dictation_strings.grdp
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <grit-part>
   <message name="DICTATION_COMMAND_DELETE_PREV_CHAR" desc="A spoken text-editing command to delete one character." is_accessibility_with_no_ui="true">
-    delete
+    delete the previous character
   </message>
   <message name="DICTATION_COMMAND_NAV_PREV_CHAR" desc="A spoken text-editing command to move the cursor previous one character." is_accessibility_with_no_ui="true">
     move to the previous character
   </message>
-  <message name="DICTATION_COMMAND_NAV_NEXT_CHAR" desc="A spoken text-editing command move the cursor next one character." is_accessibility_with_no_ui="true">
+  <message name="DICTATION_COMMAND_NAV_NEXT_CHAR" desc="A spoken text-editing command to move the cursor next one character." is_accessibility_with_no_ui="true">
     move to the next character
   </message>
     <message name="DICTATION_COMMAND_NAV_PREV_LINE" desc="A spoken text-editing command to move the cursor up one line." is_accessibility_with_no_ui="true">
@@ -45,4 +45,37 @@
   <message name="DICTATION_COMMAND_INPUT_TEXT_VIEW" desc="A spoken text-editing command to type another command's keyword. For example, 'type delete' is a command to type the word 'delete'." is_accessibility_with_no_ui="true">
     type
   </message>
+  <message name="DICTATION_COMMAND_STOP_LISTENING" desc="A spoken command to stop Dictation." is_accessibility_with_no_ui="true">
+    cancel
+  </message>
+  <message name="DICTATION_COMMAND_DELETE_PREV_WORD" desc="A spoken text-editing command to delete the previous word." is_accessibility_with_no_ui="true">
+    delete the previous word
+  </message>
+  <message name="DICTATION_COMMAND_DELETE_PREV_SENT" desc="A spoken text-editing command to delete the previous sentence." is_accessibility_with_no_ui="true">
+    delete the previous sentence
+  </message>
+  <message name="DICTATION_COMMAND_NAV_NEXT_WORD" desc="A spoken command to move the cursor to the next word." is_accessibility_with_no_ui="true">
+    move to the next word
+  </message>
+  <message name="DICTATION_COMMAND_NAV_PREV_WORD" desc="A spoken command to move the cursor to the previous word." is_accessibility_with_no_ui="true">
+    move to the previous word
+  </message>
+  <message name="DICTATION_COMMAND_SMART_DELETE_PHRASE" desc="A spoken text-editing command to delete a word or phrase." is_accessibility_with_no_ui="true">
+    delete <ph name="phrase">$1<ex>hello world</ex></ph>
+  </message>
+  <message name="DICTATION_COMMAND_SMART_REPLACE_PHRASE" desc="A spoken text-editing command to replace a word or phrase with another word or phrase." is_accessibility_with_no_ui="true">
+    replace <ph name="delete_phrase">$1<ex>hello world</ex></ph> with <ph name="insert_phrase">$2<ex>goodnight world</ex></ph>
+  </message>
+  <message name="DICTATION_COMMAND_SMART_INSERT_BEFORE" desc="A spoken text-editing command to insert a word or phrase before another word or phrase." is_accessibility_with_no_ui="true">
+    insert <ph name="insert_phrase">$1<ex>hello world</ex></ph> before <ph name="before_phrase">$2<ex>goodnight world</ex></ph>
+  </message>
+  <message name="DICTATION_COMMAND_SMART_SELECT_BTWN_INCL" desc="A spoken text-editing command to set the selection between to phrases." is_accessibility_with_no_ui="true">
+    select from <ph name="start_phrase">$1<ex>hello world</ex></ph> to <ph name="end_phrase">$2<ex>goodnight world</ex></ph>
+  </message>
+  <message name="DICTATION_COMMAND_NAV_NEXT_SENT" desc="A spoken command to move the cursor to the next sentence." is_accessibility_with_no_ui="true">
+    move to the next sentence
+  </message>
+  <message name="DICTATION_COMMAND_NAV_PREV_SENT" desc="A spoken command to move the cursor to the previous sentence." is_accessibility_with_no_ui="true">
+    move to the previous sentence
+  </message>
 </grit-part>
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
index e51490e2..08b55fd 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
@@ -51,7 +51,7 @@
   return Navigator.byItem.node_;
 }
 
-TEST_F('SwitchAccessItemScanManagerTest', 'MoveTo', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'MoveTo', async function() {
   const website = `<div id="outerGroup">
                      <div id="group">
                        <input type="text">
@@ -115,7 +115,7 @@
       'Did not find the right group');
 });
 
-TEST_F('SwitchAccessItemScanManagerTest', 'JumpTo', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'JumpTo', async function() {
   const website = `<div id="group1">
                      <input id="testinput" type="text">
                      <button></button>
@@ -153,7 +153,7 @@
       'Did not jump back to the right group.');
 });
 
-TEST_F('SwitchAccessItemScanManagerTest', 'SelectButton', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'SelectButton', async function() {
   const website = `<button id="test" aria-pressed=false>First Button</button>
       <button>Second Button</button>
       <script>
@@ -183,7 +183,7 @@
   Navigator.byItem.node_.performAction('select');
 });
 
-TEST_F('SwitchAccessItemScanManagerTest', 'EnterGroup', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'EnterGroup', async function() {
   const website = `<div id="group">
                      <button></button>
                      <button></button>
@@ -212,7 +212,7 @@
       'Did not move back to the original group');
 });
 
-TEST_F('SwitchAccessItemScanManagerTest', 'MoveForward', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'MoveForward', async function() {
   const website = `<div>
                      <button id="button1"></button>
                      <button id="button2"></button>
@@ -266,7 +266,7 @@
       'button1 should come after the BackButtonNode');
 });
 
-TEST_F('SwitchAccessItemScanManagerTest', 'MoveBackward', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'MoveBackward', async function() {
   const website = `<div>
                      <button id="button1"></button>
                      <button id="button2"></button>
@@ -318,7 +318,7 @@
       'button1 should come before button2');
 });
 
-TEST_F(
+AX_TEST_F(
     'SwitchAccessItemScanManagerTest', 'NodeUndefinedBeforeTreeChangeRemoved',
     async function() {
       const website = `<div>
@@ -346,7 +346,7 @@
           {type: chrome.automation.TreeChangeType.NODE_REMOVED});
     });
 
-TEST_F(
+AX_TEST_F(
     'SwitchAccessItemScanManagerTest', 'ScanAndTypeVirtualKeyboard',
     async function() {
       const website = `<input type="text" id="testinput"></input>`;
@@ -381,7 +381,7 @@
       }
     });
 
-TEST_F(
+AX_TEST_F(
     'SwitchAccessItemScanManagerTest', 'DismissVirtualKeyboard',
     async function() {
       const website =
@@ -429,7 +429,7 @@
     });
 
 // TODO(crbug.com/1260231): Test is flaky.
-TEST_F(
+AX_TEST_F(
     'SwitchAccessItemScanManagerTest', 'DISABLED_ChildrenChangedDoesNotRefresh',
     async function() {
       const website = `
@@ -479,11 +479,11 @@
       assertEquals(slider, Navigator.byItem.node_);
     });
 
-TEST_F('SwitchAccessItemScanManagerTest', 'InitialFocus', async function() {
+AX_TEST_F('SwitchAccessItemScanManagerTest', 'InitialFocus', async function() {
   const website = `<input></input><button autofocus></button>`;
   const rootWebArea = await this.runWithLoadedTree(website);
-  // The button should have initial focus. This ensures we move past the focus
-  // event below.
+  // The button should have initial focus. This ensures we move past the
+  // focus event below.
   const button =
       await this.untilFocusIs({role: chrome.automation.RoleType.BUTTON});
 
@@ -499,7 +499,7 @@
 });
 
 
-TEST_F(
+AX_TEST_F(
     'SwitchAccessItemScanManagerTest', 'SyncFocusToNewWindow',
     async function() {
       const website1 = `<button autofocus>one</button>`;
@@ -570,7 +570,7 @@
     });
 
 // TODO(crbug.com/1219067): Unflake.
-TEST_F(
+AX_TEST_F(
     'SwitchAccessItemScanManagerTest', 'DISABLED_LockScreenBlocksUserSession',
     async function() {
       const website = `<button autofocus>kitties!</button>`;
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index c80617d..0e057f1 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -97,6 +97,7 @@
     # Vector resources
     "images/arc_sideloading_illustration.svg",
     "images/browser_sync.svg",
+    "images/data_loss_warning.svg",
     "images/device-disabled.svg",
     "images/encryption_migration.svg",
     "images/enrollment_complete.svg",
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
index d4213e2..b1a39a2 100644
--- a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
+++ b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
@@ -84,6 +84,10 @@
         --oobe-adaptive-dialog-content-top-padding: 0;
       }
 
+      :host-context([orientation=horizontal]):host([single-column]) {
+        --oobe-adaptive-dialog-header-width: 456px;
+      }
+
       :host-context([orientation=vertical]) {
         --oobe-adaptive-dialog-content-direction: column;
         --oobe-adaptive-dialog-item-alignment: center;
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js
index dae1ad1..f01b62e0 100644
--- a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js
+++ b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.js
@@ -40,6 +40,16 @@
     },
 
     /**
+     * If set, the width of the dialog header will be wider compared to the
+     * the normal dialog in horizontal orientation.
+     */
+    singleColumn: {
+      type: Boolean,
+      reflectToAttribute: true,
+      value: false,
+    },
+
+    /**
      * if readMore is set to true and the content overflows contentContainer,
      * showReadMoreButton_ will be set to true to show the `Read more` button
      * and hide the bottom buttons.
@@ -53,7 +63,7 @@
     showReadMoreButton_: {
       type: Boolean,
       value: false,
-    }
+    },
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/login/images/data_loss_warning.svg b/chrome/browser/resources/chromeos/login/images/data_loss_warning.svg
new file mode 100644
index 0000000..d594910
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/data_loss_warning.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 1040 640"><path fill="#fff" d="M0 0h1040v640H0z"/><g clip-path="url(#a)"><path fill="#4285F4" d="m651.18 51.48 8.64 21.88 8.16 15.12 2.94 17.76s8.74-4.86 33.9.88c25.16 5.72 44.38 22.5 44.38 22.5 11.74-7.9 19.38-22.42 16.26-36.72l-.3-1.26a32.237 32.237 0 0 0-9.44-15.7c-9.56-8.8-22.38-12.24-34.9-14.28-5.84-.96-11.74-1.64-17.6-2.48-5.7-.82-11.38-1.84-16.9-3.52-5.64-1.72-11.9-4.56-17.6-6.02 0 0-4.62-2.34-16.3-.08"/><path fill="#EE5FFA" d="M326.76 362.58a8.02 8.02 0 1 0 0-16.04 8.02 8.02 0 0 0 0 16.04Z"/><path fill="#EA4335" d="m446.88 273.02-31.8-9.98c-1.12-.36-1.44-1.8-.58-2.6l24.54-22.54c.86-.8 2.28-.36 2.54.8l7.26 32.52c.26 1.16-.84 2.16-1.96 1.8Z"/><path fill="#E8EAED" d="M409.1 374.16c16.304 0 29.52-13.216 29.52-29.52 0-16.303-13.216-29.52-29.52-29.52-16.303 0-29.52 13.217-29.52 29.52 0 16.304 13.217 29.52 29.52 29.52Z"/><path stroke="#30E2EA" stroke-miterlimit="10" stroke-width="4" d="M387.64 349.34c5.02-6.2 4.06-15.3-2.16-20.32l-26.26-21.28c-6.2-5.02-15.3-4.06-20.32 2.16-5.02 6.2-4.06 15.3 2.16 20.32l26.26 21.28c6.2 5.02 15.3 4.06 20.32-2.16Z"/><path stroke="#CEE3FF" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M397.62 165.3c-8.5 15.42-14.44 32.68-14.12 50.3.3 17.62 7.48 35.58 21.34 46.44"/><path fill="#fff" d="M389.2 350.6c2.76-3.42 4.02-7.7 3.56-12.08-.46-4.38-2.6-8.3-6.02-11.06l-1.02-.84c-.82 1.08-1.58 2.18-2.24 3.34l.76.62c2.58 2.08 4.2 5.06 4.56 8.36.34 3.3-.6 6.56-2.7 9.14a12.323 12.323 0 0 1-5.58 3.9c.34 1.28.78 2.56 1.28 3.8 2.82-.98 5.4-2.7 7.4-5.2v.02Z"/><path fill="#34A853" d="m389.44 293.62 1.68 3.94c.3.76 1.14 1.18 1.96.98l2.16-.58c.28.24.54.44.82.64l.04 2.22c0 .8.66 1.5 1.5 1.6l4.22.52c.84.1 1.62-.42 1.84-1.24l.58-2.12c.32-.12.64-.26.96-.42l1.96 1.08c.76.4 1.68.18 2.16-.5l2.54-3.36c.5-.7.42-1.62-.14-2.2l-1.58-1.58c.04-.16.06-.34.08-.52.02-.18.04-.36.04-.52l1.9-1.14c.74-.44 1.02-1.34.68-2.1l-1.66-3.9c-.3-.76-1.14-1.18-1.96-.96l-2.16.58c-.28-.24-.54-.44-.82-.64l-.04-2.22c0-.82-.64-1.52-1.48-1.62l-4.22-.52c-.84-.1-1.62.42-1.84 1.24l-.58 2.12c-.32.12-.64.26-.96.42l-1.96-1.08c-.78-.4-1.7-.18-2.18.48l-2.54 3.38c-.5.7-.42 1.62.14 2.2l1.58 1.58c-.04.16-.06.34-.08.52-.02.18-.04.36-.04.52l-1.9 1.14c-.7.44-1 1.3-.68 2.08l-.02-.02Zm7.72-3.16c.26-2.2 2.28-3.78 4.48-3.52 2.2.26 3.78 2.28 3.52 4.48-.26 2.2-2.28 3.78-4.48 3.52-2.2-.26-3.78-2.28-3.52-4.48Z"/><path stroke="#2785FC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M273.64 569.66h172.34c6.62 0 12 5.38 12 12V590H261.64v-8.34c0-6.62 5.38-12 12-12Z"/><path fill="#fff" stroke="#2785FC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M277.36 357.02h164.88c2.2 0 4 1.8 4 4v208.64H273.36V361.02c0-2.2 1.8-4 4-4Z"/><path fill="#fff" stroke="#2785FC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M306.48 380.02a7.32 7.32 0 0 0-7.32-7.32 7.32 7.32 0 0 0-7.32 7.32V545.7a7.32 7.32 0 0 0 7.32 7.32 7.32 7.32 0 0 0 7.32-7.32V380.02Zm60.64 0a7.32 7.32 0 0 0-7.32-7.32 7.32 7.32 0 0 0-7.32 7.32V545.7a7.32 7.32 0 0 0 7.32 7.32 7.32 7.32 0 0 0 7.32-7.32V380.02Zm57.28 0a7.32 7.32 0 0 0-14.64 0V545.7a7.32 7.32 0 1 0 14.64 0V380.02Zm-55.16-98.658-149.212 91.437a7.999 7.999 0 0 0-2.641 11.001l.01.017a8 8 0 0 0 11.001 2.641l149.212-91.437a8 8 0 0 0 2.641-11.001l-.01-.017a8 8 0 0 0-11.001-2.641Z"/><path fill="#fff" stroke="#2785FC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M216.12 375.2c-8.04-13.14-3.92-30.3 9.2-38.36l109.46-67.08c13.14-8.04 30.3-3.92 38.36 9.2L216.12 375.2Zm54.979-77.362 8.169-5.005a3.324 3.324 0 0 1 4.565 1.096l3.135 5.116-13.847 8.485-3.135-5.116a3.324 3.324 0 0 1 1.096-4.565l.017-.011Z"/><path fill="#D2E3FC" d="M764.1 330.96c1.28 4.6 2.46 11.74 6.88 13.56.52.22 1.08.36 1.62.22.7-.2 1.14-.9 1.48-1.56 3.22-6.16 3.86-15.36 3.84-22.32 2.58 7.94 5.8 15.68 9.6 23.12 1.54 3.04 3.4 6.22 6.52 7.6 3.1 1.38 7.66-.2 7.94-3.6.06 2.46 5.02 5.68 7.34 4.86 2.32-.82 1.98-4.86 2.86-7.16-.22 1.06 2.58 2.1 3.64 2.28 1.06.18 2.14-.3 3.04-.92 3.06-2.1 2.9-6.08 2.26-9.72 3.4.88 6.5-2.92 6.46-6.44-.04-3.52-3.86-13.46-5.08-16.76-5.64-15.12-7.2-21.96-14.72-34.94-15.22 1.72-31.98 4.12-46.3 9.8-4.22 1.68-3.76 2.98-3.72 7.6.02 3.88.4 7.74.98 11.56 1.18 7.72 3.26 15.28 5.34 22.78l.02.04ZM648.02 103.78c-.26.98.56 3.22-.3 3.76-.74.46.88 0 0 0-5.04.1-10.06 1.02-14.8 2.72.88 5.22 1.9 10.76 5.46 14.7 2.68 2.96 6.56 4.68 10.5 5.26 3.94.58 12.36.08 16.26-.74 6.24-1.32 12.58-2.18 17.7-6 5.12-3.82 14.32-10.82 15.82-17.04-3.72-.38-17.16-.56-20.88-.94-.42-.04-.88-.1-1.18-.4-.22-.24-.32-.56-.4-.88l-3.42-13.54c-2.88.66-5.78 1.18-8.72 1.52-2.5.28-9.22-.92-11.6-.26-4.12 1.14-3.54 8.46-4.44 11.84Zm-171.22 98.3 2.88 40.08c-7.72 4.74-16.74 7.54-25.78 7.7-3.18.06-6.78-.4-8.66-2.96-.46-.64-.8-1.42-.62-2.18.1-.46.38-.84.66-1.22 3.54-4.52 9.52-6.2 15.1-7.64.2-.06.46-.08.56.12.1.2-.38.22-.24.06-8.72-2.5-16.3-5.96-24.56-9.74l-6.72 14.64c-.82 1.78-1.64 3.56-2.9 5.04s-3.06 2.64-5 2.68c-2.92.08-5.48-2.38-6.22-5.2-.74-2.82 0-5.84 1.28-8.48-4.36-1.86-6.88-7.22-5.52-11.78-.8-.4-1.02-1.42-1.1-2.32-.42-5.52 1.44-10.98 4.06-15.86.76-1.4 1.58-2.78 2.7-3.92 2.28-2.32 5.54-3.36 8.7-4.16 6.6-1.64 13.4-2.48 20.2-2.36 10.52.2 21.78 2.78 31.24 7.42l-.06.08Zm110.28 221.58-4.32 66.12-2.54 43.28h73.98l7.94-109.08-75.06-.32Zm90.66 0 11.08 28.88 28.6 68.16L784 491.72l-26.44-68.06h-79.82Z"/><path fill="#D2E3FC" stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M660.56 93.56c12.029 0 21.78-9.751 21.78-21.78 0-12.029-9.751-21.78-21.78-21.78-12.028 0-21.78 9.751-21.78 21.78 0 12.029 9.752 21.78 21.78 21.78Z"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M622.76 187.88s-3.56 11.5-11.66 27.96c-6.36 12.94-13.04 22.1-27.94 32.3-3.4 2.34-11.74 5.84-15.72 6.98a71.744 71.744 0 0 1-21.9 2.76c-6.82-.2-13.58-1.36-20.4-1.18-14.42.36-31.2 6.34-43.7-1.94m166.58-148.52s-4.44.98-7.06 1.38c-7.54 1.16-12.68 2.76-18.92 5.06-5.84 2.16-11.4 5.06-16.46 8.74-24.04 17.44-35.76 44.92-48.66 71.68m-75.46 61.68-3.78-53.48m-15.72 34.24c-8.5-3.22-19.24-7.26-25.9-9.38-1.46-.44-7.5 17.36-8.36 18.9-1.12 2.02-2.86 4.06-5.28 4.38-4.44.58-7.3-4-7.1-7.92.22-4.06 2.34-7.78 3.8-11.5 1.16-2.92 2.6-5.76 4.44-8.32 2.7-3.78 7.48-5.62 12.12-5.84 4.64-.22 9.22.98 13.7 2.16"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M477.36 201.3c-10.36-2.98-20.88-5.8-31.68-6.7-9.42-.78-21.24-1.06-28.4 6.42-3.96 4.14-5.86 9.82-7.5 15.3-.44 1.48-.88 3-.82 4.54.06 1.54.68 3.16 1.96 4.02"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M448.08 205.3c-4.44-.78-9-1.12-13.52-.98-3.98.12-8.1.66-11.48 2.62-2.22 1.28-4.02 3.12-5.56 5.06a34.57 34.57 0 0 0-5.52 9.96c-.8 2.26-1.36 4.68-.98 7.04.38 2.36 1.82 4.68 4.14 5.7m64.68 7.84c-6.58 5.82-17.18 6.44-28.04 6.96-1.5.08-3.04.14-4.44-.4-1.4-.54-2.64-1.82-2.62-3.32 0-1.02.6-1.96 1.26-2.72 2.3-2.68 5.7-4.08 9.02-5.26 4.5-1.6 2.28-1.16 6.94-2.26m213.78-130.12 19.42.82c5.96.24 11.9 1.02 17.66 2.5 3.32.86 5.94 1.72 7.68 2.4 33.12 12.94 51.14 47.04 60.32 79.54 3.22 11.42 7.76 21.92 13.9 32 5.76 9.46 8.2 10.88 12.46 21.12 4.26 10.3 4.4 22.66 3.72 33.66m0 0L758 290.12M477.68 201.3l79.24-8.18m150.32 2.48H627.7"/><path stroke="#2785FC" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.46" d="m784 491.72-66.58 28.98"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M677.74 423.66c8.58 25.22 39.68 97.06 39.68 97.06m-130.34-96.74c-3.34 46.38-6.86 109.06-6.86 109.06h74l7.94-109.06M784 491.72l-26.44-68.06M707.12 195.6s5.06-27.08 6.1-33.04"/><path fill="#4285F4" stroke="#4285F4" stroke-miterlimit="10" stroke-width="2" d="M650.32 52.56s3.98 22.48 30.3 27.74c0 0 6.7-12.44-6.22-25.34 0 0-9.24-9.08-24.08-2.4Z"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M641.46 79.14c2.76-.12 5.52-.34 8.18-1.04 2.66-.68 5.26-1.86 7.3-3.72m-8.92 31.86 2.64-14.02m24.62 13.2-2.64-14.02m149.38 246.72c3-1.2 4.7-4.62 4.7-7.86 0-7.62-10.72-34.42-19.72-51.88m0 24.14c6.3 12.26 12.1 22.86 14.22 35.04.44 2.58.34 5.4-.98 7.66-1.32 2.28-4.96 3.92-7.4 1.74m-26.26-37.44c5.46 10.84 11.66 22.88 14.28 34.74.64 2.86.8 6.48-1.64 8-1.5.94-3.52.64-5.04-.3s-2.62-2.42-3.64-3.9c-2.46-3.58-3.08-4.5-4.92-8.46-3.72-8.06-7.12-18.5-11.58-27.32"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M797.38 305.78c3.66 7.36 9.96 20.5 12.82 28.2 1.44 3.88 2.68 7.94 2.48 12.06-.08 1.64-.4 3.3-1.3 4.68-.9 1.36-2.46 2.38-4.1 2.26-1.84-.12-5.32-2.32-6.1-3.98m-24.78-30.98c-.06 5.92.04 13.4 0 19.32-.02 2.76-.46 6.16-3.02 7.2-1.5.6-3.28.08-4.52-.96-1.24-1.04-2.02-2.52-2.64-4.02-1.14-2.8-1.8-5.76-2.44-8.7-2.82-12.8-6.6-27.64-5.76-40.72l-53.94-153.62m-72.24-26.78c0 11.86 6.94 20.72 25.28 20.88 15.84.14 35.36-9.14 41.52-24.18m8.48 89.16c15.06 23.8 21.96 51.38 24.24 79.22 2.7 32.96-.34 65.86 9.98 98.08l16.24 50.74H587.1s2.78-87.78 28.2-181.1c11.66-42.86 12.42-47.3 12.42-47.3s-5.86-4.64-4.82-10.68c.7-4.1 1.86-8.14 2.1-12.3.24-4.44-1.06-8.8-1.7-13.2-1.18-7.96-.28-16.22 2.62-23.74"/><path fill="#4285F4" stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m784 492.06 19.76 46.18-17.4 8.98-11.5-20.88c.86 15.88-8.58 31.3-24.02 39.26-13.9 7.16-25.7 11.66-28.44 13.06-1.36-2.36-2.46-5.48-1.58-9.32.64-2.76 2.9-5.18 4.88-7.2 3.1-3.16 3.5-8.28 1.54-14.92-1.2-4.1-4.36-13.8-9.82-26.52L784 492.06v0Zm-203.78 41c-.96 14.56-1.98 22.9-2.86 27.56-1.38 7.32-6.9 9.28-9.56 10.22-2.66.94-5.72 2.16-7.52 4.48-2.5 3.24-2.96 6.78-2.82 9.72 3.02 0 19.52.02 34.88.04 17.06 0 32.24-10.1 38.66-25.74l.62 25.72h19.24l3.36-51.98h-74v-.02Z"/><path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M679 74.16c.92-4.42 0-10.08-3.34-13.38-3.2-3.16-10.7-4.6-14.9-5.86-1.86-.56-3.84-.98-5.6-1.82"/><path fill="#4285F4" d="M787.68 146.54c14.625 0 26.48-11.855 26.48-26.48 0-14.624-11.855-26.48-26.48-26.48-14.624 0-26.48 11.856-26.48 26.48 0 14.625 11.856 26.48 26.48 26.48Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h618.72v544.02H0z" transform="translate(210 48)"/></clipPath></defs></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/screens/common/marketing_opt_in.html b/chrome/browser/resources/chromeos/login/screens/common/marketing_opt_in.html
index 5609643..825ad07 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/marketing_opt_in.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/marketing_opt_in.html
@@ -127,9 +127,6 @@
           hidden="[[!isCloudGamingDevice_]]" slot="title">
         [[i18nDynamic(locale, 'marketingOptInScreenGameDeviceTitle')]]
       </h1>
-      <h1 hidden="[[!isCloudGamingDevice_]]" slot="title">
-        [[i18nDynamic(locale, 'marketingOptInScreenGameDeviceTitle2')]]
-      </h1>
       <div slot="subtitle">
         <!-- No marketing opt in -->
         <div hidden="[[isCloudGamingDevice_]]">
@@ -156,7 +153,7 @@
           <iron-icon id="toggleRowIcon" icon="oobe-32:chromebook"></iron-icon>
           <div id="chromebookUpdatesOptionLabel" class="flex layout vertical"
               aria-hidden="true">
-            <div class="toggle-title" hidden="[[!isCloudGamingDevice_]]">
+            <div class="toggle-title" hidden="[[isCloudGamingDevice_]]">
               [[i18nDynamic(locale, 'marketingOptInGetChromebookUpdates')]]
             </div>
             <div class="toggle-title" hidden="[[!isCloudGamingDevice_]]">
diff --git a/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.html b/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.html
index 388c3f4..19ce9e0 100644
--- a/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.html
+++ b/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
 <link rel="import" href="../../components/display_manager_types.html">
@@ -37,14 +38,51 @@
 -->
 <dom-module id="gaia-password-changed-element">
   <template>
-    <style include="oobe-dialog-host-styles"></style>
-    <oobe-adaptive-dialog role="dialog" for-step="password" id="passwordStep">
-      <iron-icon slot="icon" icon="oobe-32:avatar"></iron-icon>
-      <h1 slot="title">[[email]]</h1>
-      <p slot="subtitle">
-        [[i18nDynamic(locale, 'passwordChangedTitle')]]
-      </p>
-      <div slot="content" class="landscape-vertical-centered">
+    <style include="oobe-dialog-host-styles">
+      .email {
+        font-weight: bold;
+        padding-bottom: 16px;
+      }
+
+      #oldPasswordInput2 {
+        padding-top: 32px;
+      }
+
+      :host-context([orientation=vertical]) #oldPasswordInput2 {
+        padding-top: 40px;
+        text-align: initial;
+        width: calc(var(--oobe-adaptive-dialog-width) - 48px
+            - 2 * var(--oobe-adaptive-dialog-content-padding));
+      }
+    </style>
+    <oobe-adaptive-dialog role="dialog" for-step="password" id="passwordStep"
+        single-column="[[isCryptohomeRecoveryUIFlowEnabled_]]">
+      <template is="dom-if" if="[[!isCryptohomeRecoveryUIFlowEnabled_]]">
+        <iron-icon slot="icon" icon="oobe-32:avatar"></iron-icon>
+        <h1 slot="title">[[email]]</h1>
+        <p slot="subtitle">
+          [[i18nDynamic(locale, 'passwordChangedTitle')]]
+        </p>
+      </template>
+      <template is="dom-if" if="[[isCryptohomeRecoveryUIFlowEnabled_]]">
+        <iron-icon slot="icon" icon="oobe-32:lock"></iron-icon>
+        <h1 slot="title">
+          [[i18nDynamic(locale,'recoverLocalDataTitle')]]
+        </h1>
+        <div slot="subtitle">
+          <div class="email">[[email]]</div>
+          <div>[[i18nDynamic(locale, 'recoverLocalDataSubtitle')]]</div>
+        </div>
+      </template>
+      <cr-input slot="subtitle" type="password" id="oldPasswordInput2"
+          hidden="[[!isCryptohomeRecoveryUIFlowEnabled_]]" required
+          value="{{password_}}" invalid="{{passwordInvalid_}}"
+          class="focus-on-show"
+          placeholder="[[i18nDynamic(locale, 'oldPasswordHint')]]"
+          error-message="[[i18nDynamic(locale, 'oldPasswordIncorrect')]]">
+      </cr-input>
+      <div slot="content" class="landscape-vertical-centered"
+          hidden="[[isCryptohomeRecoveryUIFlowEnabled_]]">
         <cr-input type="password" id="oldPasswordInput" required
             value="{{password_}}" invalid="{{passwordInvalid_}}"
             class="focus-on-show"
@@ -57,8 +95,7 @@
         </gaia-button>
       </div>
       <div slot="bottom-buttons">
-        <oobe-text-button id="cancel" on-click="onCancel_" text-key="cancel"
-            border>
+        <oobe-text-button id="cancel" on-click="onCancel_" text-key="cancel">
         </oobe-text-button>
         <oobe-next-button id="next" on-click="submit_" inverse>
         </oobe-next-button>
@@ -67,16 +104,34 @@
 
     <oobe-adaptive-dialog role="dialog" for-step="forgot" id="forgotPassword">
       <iron-icon slot="icon" icon="oobe-32:warning"></iron-icon>
-      <h1 slot="title">[[email]]</h1>
-      <p slot="subtitle">
-        [[i18nDynamic(locale, 'passwordChangedProceedAnywayTitle')]]
-      </p>
+      <template is="dom-if" if="[[!isCryptohomeRecoveryUIFlowEnabled_]]">
+        <h1 slot="title">[[email]]</h1>
+        <p slot="subtitle">
+          [[i18nDynamic(locale, 'passwordChangedProceedAnywayTitle')]]
+        </p>
+      </template>
+      <template is="dom-if" if="[[isCryptohomeRecoveryUIFlowEnabled_]]">
+        <h1 slot="title">
+          [[i18nDynamic(locale,'dataLossWarningTitle')]]
+        </h1>
+        <div slot="subtitle">
+          <div class="email">[[email]]</div>
+          <div>[[i18nDynamic(locale, 'dataLossWarningSubtitleP1')]]</div>
+          <div>[[i18nDynamic(locale, 'dataLossWarningSubtitleP2')]]</div>
+        </div>
+        <div slot="content" class="flex layout vertical center
+            center-justified">
+          <img class="update-illustration oobe-illustration"
+            srcset="images/data_loss_warning.svg" aria-hidden="true">
+        </div>
+      </template>
       <div slot="bottom-buttons">
         <oobe-text-button id="tryAgain" on-click="onTryAgainClicked_"
             class="focus-on-show" text-key="passwordChangedTryAgain">
         </oobe-text-button>
         <oobe-text-button id="proceedAnyway" on-click="onProceedClicked_"
-            text-key="proceedAnywayButton" inverse>
+            text-key="proceedAnywayButton"
+            inverse="[[!isCryptohomeRecoveryUIFlowEnabled_]]">
         </oobe-text-button>
       </div>
     </oobe-adaptive-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.js b/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.js
index 98d6a89..e25da4d 100644
--- a/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.js
+++ b/chrome/browser/resources/chromeos/login/screens/login/gaia_password_changed.js
@@ -32,10 +32,17 @@
 /**
  * @typedef {{
  *   oldPasswordInput:  CrInputElement,
+ *   oldPasswordInput2:  CrInputElement,
+ *   cancel:  OobeTextButton,
+ *   tryAgain:  OobeTextButton,
+ *   proceedAnyway:  OobeTextButton,
  * }}
  */
 GaiaPasswordChangedBase.$;
 
+/**
+ * @polymer
+ */
 class GaiaPasswordChanged extends GaiaPasswordChangedBase {
   static get is() {
     return 'gaia-password-changed-element';
@@ -52,6 +59,13 @@
       passwordInvalid_: Boolean,
 
       disabled: Boolean,
+
+      passwordInput_: Object,
+
+      isCryptohomeRecoveryUIFlowEnabled_: {
+        type: Boolean,
+        value: loadTimeData.getBoolean('isCryptohomeRecoveryUIFlowEnabled'),
+      },
     };
   }
 
@@ -78,8 +92,11 @@
     super.ready();
     this.initializeLoginScreen('GaiaPasswordChangedScreen');
 
+    this.passwordInput_ = this.isCryptohomeRecoveryUIFlowEnabled_ ?
+        this.$.oldPasswordInput2 :
+        this.$.oldPasswordInput;
     cr.ui.LoginUITools.addSubmitListener(
-        this.$.oldPasswordInput, this.submit_.bind(this));
+        this.passwordInput_, this.submit_.bind(this));
   }
 
   /** Initial UI State for screen */
@@ -92,6 +109,11 @@
     this.reset();
     this.email = data && 'email' in data && data.email;
     this.passwordInvalid_ = data && 'showError' in data && data.showError;
+    if (this.isCryptohomeRecoveryUIFlowEnabled_) {
+      this.$.cancel.textKey = 'continueWithoutLocalDataButton';
+      this.$.tryAgain.textKey = 'oldPasswordHint';
+      this.$.proceedAnyway.textKey = 'continueAndDeleteDataButton';
+    }
   }
 
   reset() {
@@ -107,13 +129,13 @@
     if (this.disabled) {
       return;
     }
-    if (!this.$.oldPasswordInput.validate()) {
+    if (!this.passwordInput_.validate()) {
       return;
     }
     this.setUIStep(GaiaPasswordChangedUIState.PROGRESS);
     this.disabled = true;
 
-    chrome.send('migrateUserData', [this.$.oldPasswordInput.value]);
+    chrome.send('migrateUserData', [this.passwordInput_.value]);
   }
 
   /** @private */
diff --git a/chrome/browser/resources/settings/site_settings/cookie_info.ts b/chrome/browser/resources/settings/site_settings/cookie_info.ts
index 534c4f5..08445cc 100644
--- a/chrome/browser/resources/settings/site_settings/cookie_info.ts
+++ b/chrome/browser/resources/settings/site_settings/cookie_info.ts
@@ -51,6 +51,7 @@
     ['origin', 'fileSystemOrigin'], ['persistent', 'fileSystemPersistentUsage'],
     ['temporary', 'fileSystemTemporaryUsage']
   ],
+  'quota': [['origin', 'quotaOrigin'], ['totalUsage', 'quotaSize']],
   'service_worker':
       [['origin', 'serviceWorkerOrigin'], ['size', 'serviceWorkerSize']],
   'shared_worker':
diff --git a/chrome/browser/resources/settings/site_settings/site_data_details_subpage.ts b/chrome/browser/resources/settings/site_settings/site_data_details_subpage.ts
index be494e2..793b32b 100644
--- a/chrome/browser/resources/settings/site_settings/site_data_details_subpage.ts
+++ b/chrome/browser/resources/settings/site_settings/site_data_details_subpage.ts
@@ -32,6 +32,7 @@
   service_worker: loadTimeData.getString('cookieServiceWorker'),
   shared_worker: loadTimeData.getString('cookieSharedWorker'),
   media_license: loadTimeData.getString('cookieMediaLicense'),
+  quota: loadTimeData.getString('cookieQuotaStorage'),
 };
 
 /**
@@ -140,9 +141,6 @@
     if (item.type === 'cookie') {
       return item.title;
     }
-    if (item.type === 'quota') {
-      return item.totalUsage;
-    }
     return categoryLabels[item.type];
   }
 
diff --git a/chrome/browser/share/core/resources/PRESUBMIT.py b/chrome/browser/share/core/resources/PRESUBMIT.py
index 2f45ae2..e25aaac 100644
--- a/chrome/browser/share/core/resources/PRESUBMIT.py
+++ b/chrome/browser/share/core/resources/PRESUBMIT.py
@@ -7,6 +7,10 @@
 
 
 def CheckVersionUpdatedInShareTargetList(input_api, output_api):
+    # Don't report errors for "git cl presubmit --all/--files"
+    if input_api.no_diffs:
+        return []
+
     def IsShareTargetList(x):
         return (input_api.os_path.basename(
             x.LocalPath()) == 'share_targets.asciipb')
diff --git a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
index edb271d2..75c6ae9 100644
--- a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
@@ -316,6 +316,12 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
+// Flaky on Android (crbug.com/1324507).
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_ShouldSetTheOnlyClientFlag DISABLED_ShouldSetTheOnlyClientFlag
+#else
+#define MAYBE_ShouldSetTheOnlyClientFlag ShouldSetTheOnlyClientFlag
+#endif
 IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest,
                        ShouldSetTheOnlyClientFlag) {
   ASSERT_TRUE(SetupSync());
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 2fa3dd0..66acb79 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -694,13 +694,13 @@
             GetFakeServer()->AsWeakPtr()));
   }
 
-  SyncServiceImplHarness::SigninType singin_type =
+  SyncServiceImplHarness::SigninType signin_type =
       UsingExternalServers() ? SyncServiceImplHarness::SigninType::UI_SIGNIN
                              : SyncServiceImplHarness::SigninType::FAKE_SIGNIN;
 
   DCHECK(!clients_[index]);
   clients_[index] = SyncServiceImplHarness::Create(GetProfile(index), username_,
-                                                   password_, singin_type);
+                                                   password_, signin_type);
   EXPECT_NE(nullptr, GetClient(index)) << "Could not create Client " << index;
   InitializeConfigurationRefresher(index);
 
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
index d4bfccd..ed8b69c 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
@@ -15,7 +15,16 @@
 
 namespace {
 
-using TwoClientWebAppsIntegrationTestMacWinLinux =
+// Flaky on Linux Asan/Tsan, crbug.com/1300208.
+#if BUILDFLAG(IS_LINUX) && \
+    (defined(THREAD_SANITIZER) || defined(ADDRESS_SANITIZER))
+#define MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux \
+  DISABLED_TwoClientWebAppsIntegrationTestMacWinLinux
+#else
+#define MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux \
+  TwoClientWebAppsIntegrationTestMacWinLinux
+#endif
+using MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux =
     TwoClientWebAppsIntegrationTestBase;
 
 // TODO(crbug.com/1301414): Mac shims failing to launch on Mac debug and ASAN.
@@ -27,7 +36,7 @@
   WebAppIntegration_41_30SiteA_42_40Client2_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_41_30SiteA_42_40Client2_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -48,7 +57,7 @@
   WebAppIntegration_41_31SiteA_42_40Client2_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_41_31SiteA_42_40Client2_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -69,7 +78,7 @@
   WebAppIntegration_41_47SiteA_42_40Client2_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_41_47SiteA_42_40Client2_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -81,7 +90,7 @@
   helper_.CheckAppInListNotLocallyInstalled(Site::kSiteA);
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientWebAppsIntegrationTestMacWinLinux,
+IN_PROC_BROWSER_TEST_F(MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
                        WebAppIntegration_41_29SiteA_42_40Client2_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -102,7 +111,7 @@
   WebAppIntegration_41_30SiteC_42_40Client2_45SiteC
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_41_30SiteC_42_40Client2_45SiteC) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -114,7 +123,7 @@
   helper_.CheckAppInListNotLocallyInstalled(Site::kSiteC);
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientWebAppsIntegrationTestMacWinLinux,
+IN_PROC_BROWSER_TEST_F(MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
                        WebAppIntegration_41_29SiteC_42_40Client2_45SiteC) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -127,7 +136,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_46SiteA_7SiteA_11SiteA_37SiteA_18_19) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -146,7 +155,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_46SiteA_7SiteA_11SiteA_34SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -172,7 +181,7 @@
   WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_46SiteA_7SiteA_11SiteA_1SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_46SiteA_7SiteA_11SiteA_1SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -190,7 +199,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_34SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -213,7 +222,7 @@
   WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_1SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_1SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -228,7 +237,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_41_10SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -253,7 +262,7 @@
   WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_41_43SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_41_43SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -270,7 +279,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     WebAppIntegration_29SiteA_11SiteA_7SiteA_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -287,7 +296,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     WebAppIntegration_29SiteC_11SiteC_7SiteC_40Client2_45SiteC_46SiteC_11SiteC_7SiteC_37SiteC_19) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -313,7 +322,7 @@
   WebAppIntegration_30SiteC_12SiteC_7SiteC_40Client2_45SiteC_46SiteC_12SiteC_7SiteC_37SiteC_17_20
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteC_12SiteC_7SiteC_40Client2_45SiteC_46SiteC_12SiteC_7SiteC_37SiteC_17_20) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -340,7 +349,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_10SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_10SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -371,7 +380,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_43SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_43SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -402,7 +411,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_98SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_98SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -433,7 +442,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_37SiteA_17_20
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_37SiteA_17_20) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -462,7 +471,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_69SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_69SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -490,7 +499,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_35SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_35SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -518,7 +527,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_34SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_34SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -546,7 +555,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_1SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_1SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -574,7 +583,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_34SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_34SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -599,7 +608,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_1SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_1SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -624,7 +633,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -651,7 +660,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_37SiteA_18_19
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_37SiteA_18_19) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -677,7 +686,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_10SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_10SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -704,7 +713,7 @@
   WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_43SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_30SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_43SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -731,7 +740,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_10SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_10SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -762,7 +771,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_43SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_43SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -793,7 +802,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_98SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_98SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -824,7 +833,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_37SiteA_17_20
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_37SiteA_17_20) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -853,7 +862,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_69SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_69SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -881,7 +890,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_35SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_35SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -909,7 +918,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_34SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_34SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -937,7 +946,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_1SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_1SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -965,7 +974,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_34SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_34SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -990,7 +999,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_1SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_1SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1015,7 +1024,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1042,7 +1051,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_37SiteA_18_19
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_37SiteA_18_19) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1068,7 +1077,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_10SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_10SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1095,7 +1104,7 @@
   WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_43SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_31SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_43SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1122,7 +1131,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_10SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_10SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1153,7 +1162,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_43SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_43SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1184,7 +1193,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_98SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_40Client1_41_98SiteA_40Client2_102SiteA_107SiteA_46SiteA_106SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1215,7 +1224,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_37SiteA_17_20
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_37SiteA_17_20) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1244,7 +1253,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_69SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_69SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1272,7 +1281,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_35SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_35SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1300,7 +1309,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_34SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_34SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1328,7 +1337,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_1SiteA_24
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_46SiteA_7SiteA_12SiteA_1SiteA_24) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1356,7 +1365,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_34SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_34SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1381,7 +1390,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_1SiteA_22
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_1SiteA_22) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1406,7 +1415,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_10SiteA_15SiteA_40Client1_15SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1433,7 +1442,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_37SiteA_18_19
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_37SiteA_18_19) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1459,7 +1468,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_10SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_10SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
@@ -1486,7 +1495,7 @@
   WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_43SiteA_42_45SiteA
 #endif
 IN_PROC_BROWSER_TEST_F(
-    TwoClientWebAppsIntegrationTestMacWinLinux,
+    MAYBE_TwoClientWebAppsIntegrationTestMacWinLinux,
     MAYBE_WebAppIntegration_47SiteA_24_12SiteA_7SiteA_112SiteANotShown_40Client2_45SiteA_41_43SiteA_42_45SiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
diff --git a/chrome/browser/tab/web_contents_state.cc b/chrome/browser/tab/web_contents_state.cc
index ab761c6..5f767a5 100644
--- a/chrome/browser/tab/web_contents_state.cc
+++ b/chrome/browser/tab/web_contents_state.cc
@@ -267,9 +267,10 @@
     for (int i = 0; i < entry_count; ++i) {
       // Read each SerializedNavigationEntry as a separate pickle to avoid
       // optional reads of one tab bleeding into the next tab's data.
-      size_t tab_navigation_data_length = 0;
+      int tab_navigation_data_length = 0;
       const char* tab_navigation_data = nullptr;
-      if (!iter.ReadData(&tab_navigation_data, &tab_navigation_data_length)) {
+      if (!iter.ReadInt(&tab_navigation_data_length) ||
+          !iter.ReadBytes(&tab_navigation_data, tab_navigation_data_length)) {
         LOG(ERROR) << "Failed to restore tab entry from byte array. "
                    << "(SerializedNavigationEntry size="
                    << tab_navigation_data_length << ").";
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 21df24d..64f9d624 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3152,6 +3152,7 @@
       "//chromeos/ash/services/assistant:lib",
       "//chromeos/ash/services/assistant/public/cpp",
       "//chromeos/ash/services/assistant/public/mojom",
+      "//chromeos/ash/services/auth_factor_config",
       "//chromeos/components/local_search_service/public/cpp",
       "//chromeos/components/local_search_service/public/mojom",
       "//chromeos/components/onc",
diff --git a/chrome/browser/ui/android/fast_checkout/BUILD.gn b/chrome/browser/ui/android/fast_checkout/BUILD.gn
new file mode 100644
index 0000000..7692b2d
--- /dev/null
+++ b/chrome/browser/ui/android/fast_checkout/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  deps = [ "//components/browser_ui/bottomsheet/android:java" ]
+  sources = [
+    "java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutComponent.java",
+    "java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutAutofillProfile.java",
+    "java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutCreditCard.java",
+  ]
+  resources_package = "org.chromium.chrome.browser.ui.fast_checkout"
+}
diff --git a/chrome/browser/ui/android/fast_checkout/README.md b/chrome/browser/ui/android/fast_checkout/README.md
index 78c1e71..c7650bb 100644
--- a/chrome/browser/ui/android/fast_checkout/README.md
+++ b/chrome/browser/ui/android/fast_checkout/README.md
@@ -13,3 +13,23 @@
 selects one of each, which is then filled into the corresponding form while the
 user moves along the checkout flow. If the user dismisses the sheet, the
 keyboard will be shown instead (i.e. by changing the focus).
+
+
+## Folder Structure
+
+#### java/
+
+The root folder contains the public interface of this component and data that is
+used to fill it with content, e.g. Autofill Profiles, Credit Cards.
+
+Add `chrome/browser/ui/android/fast_checkout:java` as a dependency to use the
+interface and classes defined here.
+
+#### java/internal/
+
+Contains the actual implementation. Don't try to use any class defined here
+outside of this package. If you need access to any method, consider making it
+part of the public interface as defined in `FastCheckoutComponent`.
+
+This folder contains a separate [README](internal/README.md) that explains in
+detail how the architecture looks like and how to extend the component further.
diff --git a/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn b/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn
new file mode 100644
index 0000000..0e05feef
--- /dev/null
+++ b/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  deps = [
+    "//base:base_java",
+    "//build/android:build_java",
+    "//chrome/browser/ui/android/fast_checkout:java",
+    "//components/browser_ui/bottomsheet/android:java",
+  ]
+  sources = [ "java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutCoordinator.java" ]
+}
diff --git a/chrome/browser/ui/android/fast_checkout/internal/README.md b/chrome/browser/ui/android/fast_checkout/internal/README.md
index 0d3a05b..4bd367e 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/README.md
+++ b/chrome/browser/ui/android/fast_checkout/internal/README.md
@@ -7,3 +7,36 @@
 This document provides a brief overview of the architecture.
 
 [TOC]
+
+## Component structure
+
+This component follows the typical
+[MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)
+structure that is widely used in Chrome on Android. The MVC structures separates
+logic from representation:
+
+ * The [controller](#Controller) creates and connects all subcomponents and
+   performs logic that affects the visual appearance of the component.
+ * The [model](#Model) keeps all state that affects the visual appearance of the
+   component. Changes made to it are automatically propagated to the view by a
+   Model-Change-Processor (MCP) as defined in
+    `//src/ui/android/java/src/org/chromium/ui/modelutil/`
+ * The [view](#View) is the representation of the component. It enforces styles
+   and is mostly called by a view binder to set mutable properties.
+
+
+A typical request to the Fast Checkout component API that intends to change the
+visible appearance involves all component parts. For example if the API caller
+requests to show new options:
+
+1. The API caller asks the controller to show a set of
+   `autofill profiles` and `credit cards`.
+    1. The controller prepares and filters the options for display.
+    2. The controller writes the prepared options into the [model](#Model).
+2. The MCP picks up model changes.
+    1. The MCP identifies that the `AUTOFILL_PROFILE_LIST` and `CREDIT_CARD_LIST`
+       properties were changed.
+    2. The MCP uses a ViewBinder to bind each changed option to the
+       corresponding view (e.g. a TextView inside a LinearLayout).
+3. The view renders the corresponding UI.
+    1. The view may apply style, RTL settings and event handlers for click events.
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutCoordinator.java b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutCoordinator.java
new file mode 100644
index 0000000..f9e68c1
--- /dev/null
+++ b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutCoordinator.java
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.fast_checkout;
+
+import android.content.Context;
+
+import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutAutofillProfile;
+import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutCreditCard;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+
+class FastCheckoutCoordinator implements FastCheckoutComponent {
+    @Override
+    public void initialize(Context context, BottomSheetController sheetController,
+            FastCheckoutComponent.Delegate delegate) {
+        // TODO(crbug.com/1334642): Implement.
+    }
+
+    @Override
+    public void showOptions(
+            FastCheckoutAutofillProfile[] profiles, FastCheckoutCreditCard[] creditCards) {
+        // TODO(crbug.com/1334642): Implement.
+    }
+}
diff --git a/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutComponent.java b/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutComponent.java
new file mode 100644
index 0000000..dcc5a59
--- /dev/null
+++ b/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutComponent.java
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.fast_checkout;
+
+import android.content.Context;
+
+import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutAutofillProfile;
+import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutCreditCard;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+
+/**
+ * This component supports the user during checkout flows by offering a pre-selection of addresses
+ * and credit cards and autofilling the chosen data. Data is selected via a bottom sheet that
+ * suppresses the keyboard until dismissed.
+ */
+public interface FastCheckoutComponent {
+    /**
+     * This delegate is called when the FastCheckout component is interacted with (e.g. dismissed or
+     * suggestion selected).
+     */
+    interface Delegate {
+        /**
+         * Called when the user makes a selection in the FastCheckoutComponent. The component (i.e.
+         * the bottom sheet) gets closed after selection.
+         * @param profile The selected {@link FastCheckoutAutofillProfile}.
+         * @param creditCard The selected {@link FastCheckoutCreditCard}.
+         */
+        void onOptionsSelected(
+                FastCheckoutAutofillProfile profile, FastCheckoutCreditCard creditCard);
+        /**
+         * Called when the user dismisses the FastCheckoutComponent. Not called if an option was
+         * selected.
+         */
+        void onDismissed();
+    }
+
+    /**
+     * Initializes the component.
+     * @param context A {@link Context} to create views and retrieve resources.
+     * @param sheetController A {@link BottomSheetController} used to show/hide the sheet.
+     * @param delegate A {@link Delegate} that handles events.
+     */
+    void initialize(Context context, BottomSheetController sheetController, Delegate delegate);
+
+    /**
+     * Displays the given options in a new bottom sheet.
+     */
+    void showOptions(FastCheckoutAutofillProfile[] profiles, FastCheckoutCreditCard[] creditCards);
+}
diff --git a/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutAutofillProfile.java b/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutAutofillProfile.java
new file mode 100644
index 0000000..90f7e97
--- /dev/null
+++ b/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutAutofillProfile.java
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.fast_checkout.data;
+
+/**
+ * A profile, similar to the one used by the PersonalDataManager.
+ */
+public class FastCheckoutAutofillProfile {
+    private final String mGUID;
+    private final String mFullName;
+    private final String mStreetAddress;
+    private final String mRegion;
+    private final String mLocality;
+    private final String mDependentLocality;
+    private final String mPostalCode;
+    private final String mCountryCode;
+    private final String mPhoneNumber;
+    private final String mEmailAddress;
+
+    public FastCheckoutAutofillProfile(String guid, String fullName, String streetAddress,
+            String region, String locality, String dependentLocality, String postalCode,
+            String countryCode, String phoneNumber, String emailAddress) {
+        mGUID = guid;
+        mFullName = fullName;
+        mStreetAddress = streetAddress;
+        mRegion = region;
+        mLocality = locality;
+        mDependentLocality = dependentLocality;
+        mPostalCode = postalCode;
+        mCountryCode = countryCode;
+        mPhoneNumber = phoneNumber;
+        mEmailAddress = emailAddress;
+    }
+
+    public String getGUID() {
+        return mGUID;
+    }
+
+    public String getFullName() {
+        return mFullName;
+    }
+
+    public String getStreetAddress() {
+        return mStreetAddress;
+    }
+
+    public String getRegion() {
+        return mRegion;
+    }
+
+    public String getLocality() {
+        return mLocality;
+    }
+
+    public String getDependentLocality() {
+        return mDependentLocality;
+    }
+
+    public String getPostalCode() {
+        return mPostalCode;
+    }
+
+    public String getCountryCode() {
+        return mCountryCode;
+    }
+
+    public String getPhoneNumber() {
+        return mPhoneNumber;
+    }
+
+    public String getEmailAddress() {
+        return mEmailAddress;
+    }
+}
diff --git a/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutCreditCard.java b/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutCreditCard.java
new file mode 100644
index 0000000..b12940f
--- /dev/null
+++ b/chrome/browser/ui/android/fast_checkout/java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutCreditCard.java
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ui.fast_checkout.data;
+
+/**
+ * A credit card, similar to the one used by the PersonalDataManager.
+ */
+public class FastCheckoutCreditCard {
+    private final String mGUID;
+    private final String mOrigin;
+    private final String mName;
+    private final String mNumber;
+    private final String mObfuscatedNumber;
+    private final String mMonth;
+    private final String mYear;
+    private final String mBasicCardIssuerNetwork;
+    private final String mIssuerIconString;
+    private final String mNickname;
+
+    public FastCheckoutCreditCard(String guid, String origin, String name, String number,
+            String obfuscatedNumber, String month, String year, String basicCardIssuerNetwork,
+            String issuerIconString, String nickname) {
+        mGUID = guid;
+        mOrigin = origin;
+        mName = name;
+        mNumber = number;
+        mObfuscatedNumber = obfuscatedNumber;
+        mMonth = month;
+        mYear = year;
+        mBasicCardIssuerNetwork = basicCardIssuerNetwork;
+        mIssuerIconString = issuerIconString;
+        mNickname = nickname;
+    }
+
+    public String getGUID() {
+        return mGUID;
+    }
+
+    public String getOrigin() {
+        return mOrigin;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public String getNumber() {
+        return mNumber;
+    }
+
+    public String getObfuscatedNumber() {
+        return mObfuscatedNumber;
+    }
+
+    public String getMonth() {
+        return mMonth;
+    }
+
+    public String getYear() {
+        return mYear;
+    }
+
+    public String getBasicCardIssuerNetwork() {
+        return mBasicCardIssuerNetwork;
+    }
+
+    public String getIssuerIconString() {
+        return mIssuerIconString;
+    }
+
+    public String getNickname() {
+        return mNickname;
+    }
+}
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc
index 5ac3110..6e0a7ee 100644
--- a/chrome/browser/ui/signin_view_controller.cc
+++ b/chrome/browser/ui/signin_view_controller.cc
@@ -233,6 +233,14 @@
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+void SigninViewController::ShowModalProfileCustomizationDialog() {
+  CloseModalSignin();
+  dialog_ = std::make_unique<SigninModalDialogImpl>(
+      SigninViewControllerDelegate::CreateProfileCustomizationDelegate(
+          browser_, /*show_profile_switch_iph=*/true),
+      GetOnModalDialogClosedCallback());
+}
+
 void SigninViewController::ShowModalSigninEmailConfirmationDialog(
     const std::string& last_email,
     const std::string& email,
diff --git a/chrome/browser/ui/signin_view_controller.h b/chrome/browser/ui/signin_view_controller.h
index 30af4b1..10a682b 100644
--- a/chrome/browser/ui/signin_view_controller.h
+++ b/chrome/browser/ui/signin_view_controller.h
@@ -126,6 +126,10 @@
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Shows the modal profile customization dialog as a browser-modal dialog on
+  // top of the |browser_|'s window.
+  void ShowModalProfileCustomizationDialog();
+
   // Shows the modal sign-in email confirmation dialog as a tab-modal dialog on
   // top of the currently displayed WebContents in |browser_|.
   void ShowModalSigninEmailConfirmationDialog(
diff --git a/chrome/browser/ui/signin_view_controller_delegate.h b/chrome/browser/ui/signin_view_controller_delegate.h
index ebc1408c..e4607ef 100644
--- a/chrome/browser/ui/signin_view_controller_delegate.h
+++ b/chrome/browser/ui/signin_view_controller_delegate.h
@@ -66,13 +66,18 @@
       Browser* browser,
       const CoreAccountId& account_id,
       signin_metrics::ReauthAccessPoint access_point);
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
   // Returns a platform-specific SigninViewControllerDelegate instance that
   // displays the profile customization modal dialog. The returned object should
   // delete itself when the window it's managing is closed.
+  // If `show_profile_switch_iph` is true, shows a profile switch IPH after the
+  // user completes the profile customization.
   static SigninViewControllerDelegate* CreateProfileCustomizationDelegate(
-      Browser* browser);
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+      Browser* browser,
+      bool show_profile_switch_iph = false);
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/views/profiles/profile_creation_signed_in_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_creation_signed_in_flow_controller.cc
index 0886cee..8623d6b 100644
--- a/chrome/browser/ui/views/profiles/profile_creation_signed_in_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_creation_signed_in_flow_controller.cc
@@ -48,12 +48,11 @@
     // no conflict with a synced theme / color.
     ProfileCustomizationBubbleSyncController::
         ApplyColorAndShowBubbleWhenNoValueSynced(
-            browser->profile(), anchor_view,
+            browser, anchor_view,
             /*suggested_profile_color=*/new_profile_color.value());
   } else {
     // For non syncing users, simply show the bubble.
-    ProfileCustomizationBubbleView::CreateBubble(browser->profile(),
-                                                 anchor_view);
+    ProfileCustomizationBubbleView::CreateBubble(browser, anchor_view);
   }
 }
 
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc
index b07abd8..afc55be2 100644
--- a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc
@@ -16,14 +16,14 @@
 
 namespace {
 
-void ShowBubble(Profile* profile,
+void ShowBubble(Browser* browser,
                 views::View* anchor_view,
                 ProfileCustomizationBubbleSyncController::Outcome outcome) {
   switch (outcome) {
     case ProfileCustomizationBubbleSyncController::Outcome::kAbort:
       return;
     case ProfileCustomizationBubbleSyncController::Outcome::kShowBubble:
-      ProfileCustomizationBubbleView::CreateBubble(profile, anchor_view);
+      ProfileCustomizationBubbleView::CreateBubble(browser, anchor_view);
       return;
     case ProfileCustomizationBubbleSyncController::Outcome::kSkipBubble:
       // If the customization bubble is not shown, show the IPH now. Otherwise
@@ -43,20 +43,22 @@
 
 // static
 void ProfileCustomizationBubbleSyncController::
-    ApplyColorAndShowBubbleWhenNoValueSynced(Profile* profile,
+    ApplyColorAndShowBubbleWhenNoValueSynced(Browser* browser,
                                              views::View* anchor_view,
                                              SkColor suggested_profile_color) {
+  DCHECK(browser);
+  Profile* profile = browser->profile();
   syncer::SyncService* sync_service =
       SyncServiceFactory::GetForProfile(profile);
   // TODO(crbug.com/1213112): A speculative fix, remove if not functional or not
   // needed.
-  if (!profile || !anchor_view || !sync_service)
+  if (!anchor_view || !sync_service)
     return;
   // The controller is owned by itself.
   auto* controller = new ProfileCustomizationBubbleSyncController(
       profile, anchor_view, sync_service,
       ThemeServiceFactory::GetForProfile(profile),
-      base::BindOnce(&ShowBubble, profile, anchor_view),
+      base::BindOnce(&ShowBubble, browser, anchor_view),
       suggested_profile_color);
   controller->Init();
 }
@@ -188,6 +190,6 @@
                                  ->GetAvatarToolbarButton();
   CHECK(anchor_view);
   ProfileCustomizationBubbleSyncController::
-      ApplyColorAndShowBubbleWhenNoValueSynced(browser->profile(), anchor_view,
+      ApplyColorAndShowBubbleWhenNoValueSynced(browser, anchor_view,
                                                suggested_profile_color);
 }
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h
index d07c3ca6..aa36887f 100644
--- a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h
@@ -22,6 +22,7 @@
 class View;
 }  // namespace views
 
+class Browser;
 class Profile;
 
 // Helper class for logic to show / delay showing the profile customization
@@ -52,7 +53,7 @@
   // includes the case when sync can start but is blocked on the user to enter
   // the custom passphrase.
   static void ApplyColorAndShowBubbleWhenNoValueSynced(
-      Profile* profile,
+      Browser* browser,
       views::View* anchor_view,
       SkColor suggested_profile_color);
 
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_view.cc b/chrome/browser/ui/views/profiles/profile_customization_bubble_view.cc
index 46517529..a48a50f 100644
--- a/chrome/browser/ui/views/profiles/profile_customization_bubble_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_view.cc
@@ -6,6 +6,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
+#include "chrome/browser/signin/signin_features.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
@@ -32,10 +33,21 @@
 
 // static
 ProfileCustomizationBubbleView* ProfileCustomizationBubbleView::CreateBubble(
-    Profile* profile,
+    Browser* browser,
     views::View* anchor_view) {
+  // With `kSyncPromoAfterSigninIntercept` enabled, the profile customization
+  // should be always displayed in a modal dialog. Once the feature is launched,
+  // `ProfileCustomizationBubbleView` will be removed and all the callers will
+  // migrate to `ShowModalProfileCustomizationDialog()`.
+  // Return value is only used in tests, so it's fine to return nullptr if a
+  // `ProfileCustomizationBubbleView` was not created.
+  if (base::FeatureList::IsEnabled(kSyncPromoAfterSigninIntercept)) {
+    browser->signin_view_controller()->ShowModalProfileCustomizationDialog();
+    return nullptr;
+  }
+
   ProfileCustomizationBubbleView* bubble_view =
-      new ProfileCustomizationBubbleView(profile, anchor_view);
+      new ProfileCustomizationBubbleView(browser->profile(), anchor_view);
   // The widget is owned by the views system.
   views::Widget* widget =
       views::BubbleDialogDelegateView::CreateBubble(bubble_view);
@@ -101,5 +113,5 @@
                                  ->toolbar_button_provider()
                                  ->GetAvatarToolbarButton();
   DCHECK(anchor_view);
-  ProfileCustomizationBubbleView::CreateBubble(browser->profile(), anchor_view);
+  ProfileCustomizationBubbleView::CreateBubble(browser, anchor_view);
 }
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_view.h b/chrome/browser/ui/views/profiles/profile_customization_bubble_view.h
index ea1ae53..7939ffbf 100644
--- a/chrome/browser/ui/views/profiles/profile_customization_bubble_view.h
+++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_view.h
@@ -14,6 +14,7 @@
 class View;
 }  // namespace views
 
+class Browser;
 class Profile;
 
 // This bubble is implemented as a WebUI page rendered inside a native bubble.
@@ -28,7 +29,7 @@
   ~ProfileCustomizationBubbleView() override;
 
   // Creates and shows the bubble.
-  static ProfileCustomizationBubbleView* CreateBubble(Profile* profile,
+  static ProfileCustomizationBubbleView* CreateBubble(Browser* browser,
                                                       views::View* anchor_view);
 
  private:
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_customization_bubble_view_browsertest.cc
index 8a390c65..8475d5ba 100644
--- a/chrome/browser/ui/views/profiles/profile_customization_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_view_browsertest.cc
@@ -2,22 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
 #include "chrome/browser/ui/views/profiles/profile_customization_bubble_view.h"
 
 #include <string>
+#include <vector>
 
 #include "base/callback_list.h"
+#include "base/feature_list.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/signin/signin_features.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/signin_view_controller.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h"
 #include "chrome/browser/ui/views/user_education/browser_feature_promo_controller.h"
+#include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
 #include "chrome/browser/ui/webui/signin/profile_customization_handler.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/tracker.h"
@@ -35,21 +40,27 @@
 
 }  // namespace
 
-class ProfileCustomizationBubbleBrowserTest : public DialogBrowserTest {
+class ProfileCustomizationBrowserTest : public DialogBrowserTest {
  public:
-  ProfileCustomizationBubbleBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        feature_engagement::kIPHProfileSwitchFeature);
+  explicit ProfileCustomizationBrowserTest(bool dialog_enabled) {
+    std::vector<base::Feature> enabled_features = {
+        feature_engagement::kIPHProfileSwitchFeature};
+    std::vector<base::Feature> disabled_features = {};
+    if (dialog_enabled) {
+      enabled_features.push_back(kSyncPromoAfterSigninIntercept);
+    } else {
+      disabled_features.push_back(kSyncPromoAfterSigninIntercept);
+    }
+    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
     subscription_ =
         BrowserContextDependencyManager::GetInstance()
             ->RegisterCreateServicesCallbackForTesting(base::BindRepeating(
-                &ProfileCustomizationBubbleBrowserTest::RegisterTestTracker));
+                &ProfileCustomizationBrowserTest::RegisterTestTracker));
   }
 
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    ProfileCustomizationBubbleView::CreateBubble(browser()->profile(),
-                                                 GetAvatarButton());
+    ProfileCustomizationBubbleView::CreateBubble(browser(), GetAvatarButton());
   }
 
   // Returns the avatar button, which is the anchor view for the customization
@@ -73,6 +84,13 @@
   base::CallbackListSubscription subscription_;
 };
 
+class ProfileCustomizationBubbleBrowserTest
+    : public ProfileCustomizationBrowserTest {
+ public:
+  ProfileCustomizationBubbleBrowserTest()
+      : ProfileCustomizationBrowserTest(/*dialog_enabled=*/false) {}
+};
+
 #if BUILDFLAG(IS_WIN)
 #define MAYBE_InvokeUi_default DISABLED_InvokeUi_default
 #else
@@ -88,7 +106,7 @@
   auto lock = BrowserFeaturePromoController::BlockActiveWindowCheckForTesting();
   // Create the customization bubble, owned by the view hierarchy.
   ProfileCustomizationBubbleView* bubble =
-      ProfileCustomizationBubbleView::CreateBubble(browser()->profile(),
+      ProfileCustomizationBubbleView::CreateBubble(browser(),
                                                    GetAvatarButton());
 
   feature_engagement::Tracker* tracker =
@@ -116,3 +134,44 @@
       tracker->GetTriggerState(feature_engagement::kIPHProfileSwitchFeature),
       feature_engagement::Tracker::TriggerState::HAS_BEEN_DISPLAYED);
 }
+
+class ProfileCustomizationDialogBrowserTest
+    : public ProfileCustomizationBrowserTest {
+ public:
+  ProfileCustomizationDialogBrowserTest()
+      : ProfileCustomizationBrowserTest(/*dialog_enabled=*/true) {}
+};
+
+IN_PROC_BROWSER_TEST_F(ProfileCustomizationDialogBrowserTest, IPH) {
+  AvatarToolbarButton::SetIPHMinDelayAfterCreationForTesting(base::Seconds(0));
+  auto lock = BrowserFeaturePromoController::BlockActiveWindowCheckForTesting();
+  // Create the customization dialog, owned by the view hierarchy.
+  ProfileCustomizationBubbleView::CreateBubble(browser(), GetAvatarButton());
+
+  EXPECT_TRUE(browser()->signin_view_controller()->ShowsModalDialog());
+
+  feature_engagement::Tracker* tracker =
+      BrowserView::GetBrowserViewForBrowser(browser())
+          ->GetFeaturePromoController()
+          ->feature_engagement_tracker();
+
+  EXPECT_NE(
+      tracker->GetTriggerState(feature_engagement::kIPHProfileSwitchFeature),
+      feature_engagement::Tracker::TriggerState::HAS_BEEN_DISPLAYED);
+
+  ASSERT_TRUE(
+      login_ui_test_utils::CompleteProfileCustomizationDialog(browser()));
+
+  base::RunLoop loop;
+  tracker->AddOnInitializedCallback(
+      base::BindLambdaForTesting([&loop](bool success) {
+        DCHECK(success);
+        loop.Quit();
+      }));
+  loop.Run();
+
+  ASSERT_TRUE(tracker->IsInitialized());
+  EXPECT_EQ(
+      tracker->GetTriggerState(feature_engagement::kIPHProfileSwitchFeature),
+      feature_engagement::Tracker::TriggerState::HAS_BEEN_DISPLAYED);
+}
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index 3818ef1..e1028dd 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/signin_view_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -65,14 +66,20 @@
              : kSigninErrorDialogHeight;
 }
 
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 void CloseModalSigninInBrowser(
     base::WeakPtr<Browser> browser,
+    bool show_profile_switch_iph,
     ProfileCustomizationHandler::CustomizationResult result) {
-  if (browser)
-    browser->signin_view_controller()->CloseModalSignin();
+  if (!browser)
+    return;
+
+  browser->signin_view_controller()->CloseModalSignin();
+  if (show_profile_switch_iph) {
+    browser->window()->MaybeShowProfileSwitchIPH();
+  }
 }
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 // This layout auto-resizes the host view widget to always adapt to changes in
 // the size of the child views.
@@ -120,11 +127,14 @@
                              kReauthDialogHeight, kReauthDialogWidth,
                              InitializeSigninWebDialogUI(false));
 }
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 // static
 std::unique_ptr<views::WebView>
 SigninViewControllerDelegateViews::CreateProfileCustomizationWebView(
-    Browser* browser) {
+    Browser* browser,
+    bool show_profile_switch_iph) {
   std::unique_ptr<views::WebView> web_view = CreateDialogWebView(
       browser, GURL(chrome::kChromeUIProfileCustomizationURL),
       ProfileCustomizationUI::kPreferredHeight,
@@ -136,11 +146,12 @@
                                        ->GetController()
                                        ->GetAs<ProfileCustomizationUI>();
   DCHECK(web_ui);
-  web_ui->Initialize(
-      base::BindOnce(&CloseModalSigninInBrowser, browser->AsWeakPtr()));
+  web_ui->Initialize(base::BindOnce(&CloseModalSigninInBrowser,
+                                    browser->AsWeakPtr(),
+                                    show_profile_switch_iph));
   return web_view;
 }
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -409,17 +420,20 @@
           browser, access_point),
       browser, ui::MODAL_TYPE_CHILD, false, true);
 }
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 // static
 SigninViewControllerDelegate*
 SigninViewControllerDelegate::CreateProfileCustomizationDelegate(
-    Browser* browser) {
+    Browser* browser,
+    bool show_profile_switch_iph) {
   return new SigninViewControllerDelegateViews(
       SigninViewControllerDelegateViews::CreateProfileCustomizationWebView(
-          browser),
+          browser, show_profile_switch_iph),
       browser, ui::MODAL_TYPE_WINDOW, false, false);
 }
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
index 824097c..f5adfc0 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
@@ -66,11 +66,14 @@
   static std::unique_ptr<views::WebView> CreateReauthConfirmationWebView(
       Browser* browser,
       signin_metrics::ReauthAccessPoint);
-
-  static std::unique_ptr<views::WebView> CreateProfileCustomizationWebView(
-      Browser* browser);
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  static std::unique_ptr<views::WebView> CreateProfileCustomizationWebView(
+      Browser* browser,
+      bool show_profile_switch_iph = false);
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS_LACROS)
   static std::unique_ptr<views::WebView> CreateEnterpriseConfirmationWebView(
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui_browsertest.cc b/chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui_browsertest.cc
index ab02ab3..aa51623 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui_browsertest.cc
@@ -46,7 +46,7 @@
   // Wait for reset-fake-button to become visible
   constexpr char kGetResetButtonDisplayStyle[] =
       "document.getElementById('reset-cache-fake-button').style.display";
-  while ("inline-block" != EvalJs(kGetResetButtonDisplayStyle))
+  while ("inline" != EvalJs(kGetResetButtonDisplayStyle))
     SpinRunLoop();
 
   // Trigger reset button.
diff --git a/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_chromeos.cc b/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_chromeos.cc
index 8deab0183..c3d61dc 100644
--- a/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_chromeos.cc
+++ b/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_chromeos.cc
@@ -22,7 +22,9 @@
 #include "chrome/browser/ash/web_applications/terminal_ui.h"
 
 #if !defined(OFFICIAL_BUILD)
+#include "ash/webui/demo_mode_app_ui/demo_mode_app_untrusted_ui.h"
 #include "ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.h"
+#include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #endif  // !defined(OFFICIAL_BUILD)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -58,6 +60,9 @@
 #if !defined(OFFICIAL_BUILD)
   map.AddUntrustedWebUIConfig(
       std::make_unique<ash::UntrustedSampleSystemWebAppUIConfig>());
+  map.AddUntrustedWebUIConfig(
+      std::make_unique<ash::DemoModeAppUntrustedUIConfig>(base::BindRepeating(
+          [] { return ash::DemoSession::Get()->DemoAppComponentPath(); })));
 #endif  // !defined(OFFICIAL_BUILD)
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc b/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc
index 54b9128..ed9f1501 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc
@@ -5,12 +5,10 @@
 #include "chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_chromeos.h"
 
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "content/public/browser/webui_config_map.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #if !defined(OFFICIAL_BUILD)
-#include "ash/webui/demo_mode_app_ui/demo_mode_app_ui.h"
 #include "ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.h"
 #endif  // !defined(OFFICIAL_BUILD)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -22,9 +20,6 @@
 #if !defined(OFFICIAL_BUILD)
   auto& map = content::WebUIConfigMap::GetInstance();
   map.AddWebUIConfig(std::make_unique<ash::SampleSystemWebAppUIConfig>());
-  map.AddWebUIConfig(
-      std::make_unique<ash::DemoModeAppUIConfig>(base::BindRepeating(
-          [] { return ash::DemoSession::Get()->DemoAppComponentPath(); })));
 #endif  // !defined(OFFICIAL_BUILD)
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 2d79b7bb9..f4193fe 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -1566,11 +1566,7 @@
       GURL(chrome::kOsUISyncInternalsURL), GURL(chrome::kOsUISysInternalsUrl),
       GURL(chrome::kOsUITermsURL), GURL(chrome::kChromeUIUserImageURL),
       GURL(chrome::kOsUIVersionURL), GURL(chrome::kChromeUIVmUrl),
-      GURL(chrome::kOsUISystemURL), GURL(chrome::kOsUIHelpAppURL),
-      // The CL to land this didn't land yet. Once landed they need to be moved
-      // to Lacros. However  - as the refactor might precede this, there is no
-      // TODO for it.
-      GURL(chrome::kChromeUICertificateManagerDialogURL)};
+      GURL(chrome::kOsUISystemURL), GURL(chrome::kOsUIHelpAppURL)};
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
   return std::vector<GURL>{GURL(chrome::kChromeUIAboutURL),
                            GURL(chrome::kChromeUIComponentsUrl),
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.cc
index 8c28ceb..b741462d7 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.h"
 
+#include "ash/constants/ash_features.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
 #include "chrome/browser/ash/login/screens/gaia_password_changed_screen.h"
@@ -43,6 +44,20 @@
   builder->Add("passwordChangedProceedAnywayTitle",
                IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY);
   builder->Add("passwordChangedTryAgain", IDS_LOGIN_PASSWORD_CHANGED_TRY_AGAIN);
+  builder->Add("dataLossWarningTitle",
+               IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_TITLE);
+  builder->Add("dataLossWarningSubtitleP1",
+               IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P1);
+  builder->Add("dataLossWarningSubtitleP2",
+               IDS_LOGIN_PASSWORD_CHANGED_DATA_LOSS_WARNING_SUBTITLE_P2);
+  builder->Add("recoverLocalDataTitle",
+               IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_TITLE);
+  builder->Add("recoverLocalDataSubtitle",
+               IDS_LOGIN_PASSWORD_CHANGED_RECOVER_DATA_SUBTITLE);
+  builder->Add("continueAndDeleteDataButton",
+               IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_AND_DELETE_BUTTON);
+  builder->Add("continueWithoutLocalDataButton",
+               IDS_LOGIN_PASSWORD_CHANGED_CONTINUE_WITHOUT_LOCAL_DATA_BUTTON);
 }
 
 void GaiaPasswordChangedScreenHandler::InitializeDeprecated() {
@@ -50,6 +65,13 @@
               &GaiaPasswordChangedScreenHandler::HandleMigrateUserData);
 }
 
+void GaiaPasswordChangedScreenHandler::GetAdditionalParameters(
+    base::Value::Dict* dict) {
+  dict->Set("isCryptohomeRecoveryUIFlowEnabled",
+            ash::features::IsCryptohomeRecoveryFlowUIEnabled());
+  BaseScreenHandler::GetAdditionalParameters(dict);
+}
+
 void GaiaPasswordChangedScreenHandler::Show(const std::string& email,
                                             bool has_error) {
   base::Value::Dict data;
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.h
index 6feb362..35bc39d 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_password_changed_screen_handler.h
@@ -56,6 +56,7 @@
   void DeclareLocalizedValues(
       ::login::LocalizedValuesBuilder* builder) override;
   void InitializeDeprecated() override;
+  void GetAdditionalParameters(base::Value::Dict* dict) override;
 
   ash::GaiaPasswordChangedScreen* screen_ = nullptr;
 };
diff --git a/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc
index 28f0e8fa..7f96395 100644
--- a/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.cc
@@ -48,15 +48,12 @@
     ::login::LocalizedValuesBuilder* builder) {
   builder->Add("marketingOptInScreenTitle",
                IDS_LOGIN_MARKETING_OPT_IN_SCREEN_TITLE);
-  builder->Add(
-      "marketingOptInScreenGameDeviceTitle",
-      IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_1);
-  builder->Add(
-      "marketingOptInScreenGameDeviceTitle2",
-      IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE_2);
-  builder->Add(
+  builder->Add("marketingOptInScreenGameDeviceTitle",
+               IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_TITLE);
+  builder->AddF(
       "marketingOptInScreenGameDeviceSubtitle",
-      IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE);
+      IDS_LOGIN_MARKETING_OPT_IN_SCREEN_WITH_CLOUDGAMINGDEVICE_SUBTITLE,
+      ui::GetChromeOSDeviceName());
   builder->AddF("marketingOptInScreenSubtitle",
                 IDS_LOGIN_MARKETING_OPT_IN_SCREEN_SUBTITLE,
                 ui::GetChromeOSDeviceName());
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chrome/browser/ui/webui/cookies_tree_model_util.cc
index 6bf2095..10da4099 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -80,13 +80,8 @@
 }
 
 absl::optional<base::Value::Dict>
-CookiesTreeModelUtil::GetCookieTreeNodeDictionary(const CookieTreeNode& node,
-                                                  bool include_quota_nodes) {
+CookiesTreeModelUtil::GetCookieTreeNodeDictionary(const CookieTreeNode& node) {
   base::Value::Dict dict;
-  // Use node's address as an id for WebUI to look it up.
-  dict.Set(kKeyId, GetTreeNodeId(&node));
-  dict.Set(kKeyTitle, node.GetTitle());
-  dict.Set(kKeyHasChildren, !node.children().empty());
 
   switch (node.GetDetailedInfo().node_type) {
     case CookieTreeNode::DetailedInfo::TYPE_HOST: {
@@ -177,9 +172,6 @@
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_QUOTA: {
-      if (!include_quota_nodes)
-        return absl::nullopt;
-
       dict.Set(kKeyType, "quota");
 
       const BrowsingDataQuotaHelper::QuotaInfo& quota_info =
@@ -259,26 +251,44 @@
   }
 #endif
 
+  // Only node types with detailed information above result in a dict.
+  if (dict.empty())
+    return absl::nullopt;
+
+  dict.Set(kKeyId, GetTreeNodeId(&node));
+  dict.Set(kKeyTitle, node.GetTitle());
+  dict.Set(kKeyHasChildren, !node.children().empty());
+
   return dict;
 }
 
-base::Value::List CookiesTreeModelUtil::GetChildNodeDetails(
-    const CookieTreeNode* parent,
-    bool include_quota_nodes) {
+base::Value::List CookiesTreeModelUtil::GetChildNodeDetailsDeprecated(
+    const CookieTreeNode* parent) {
   base::Value::List list;
   std::string id_path = GetTreeNodeId(parent);
   for (const auto& child : parent->children()) {
-    std::string cookie_id_path =
-        id_path + "," + GetTreeNodeId(child.get()) + ",";
+    // Node types of interest either live at this level, or the level below.
+    // Whether a node is of interest is determined by
+    // GetCookieTreeNodeDictionary().
+    std::string cookie_id_path = id_path + "," + GetTreeNodeId(child.get());
+    absl::optional<base::Value::Dict> child_dict =
+        GetCookieTreeNodeDictionary(*child);
+    if (child_dict) {
+      child_dict->Set("idPath", cookie_id_path);
+      list.Append(std::move(*child_dict));
+    }
+    cookie_id_path += ",";
+
     for (const auto& details : child->children()) {
-      absl::optional<base::Value::Dict> dict =
-          GetCookieTreeNodeDictionary(*details, include_quota_nodes);
-      if (dict) {
+      absl::optional<base::Value::Dict> details_dict =
+          GetCookieTreeNodeDictionary(*details);
+      if (details_dict) {
         // TODO(dschuyler): This ID path is an artifact from using tree nodes to
         // hold the cookies. Can this be changed to a dictionary with a key
         // lookup (and remove use of id_map_)?
-        dict->Set("idPath", cookie_id_path + GetTreeNodeId(details.get()));
-        list.Append(std::move(*dict));
+        details_dict->Set("idPath",
+                          cookie_id_path + GetTreeNodeId(details.get()));
+        list.Append(std::move(*details_dict));
       }
     }
   }
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.h b/chrome/browser/ui/webui/cookies_tree_model_util.h
index 89ed8c11..ed5558e 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.h
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.h
@@ -29,8 +29,10 @@
   std::string GetTreeNodeId(const CookieTreeNode* node);
 
   // Return the details of the child nodes of `parent`.
-  base::Value::List GetChildNodeDetails(const CookieTreeNode* parent,
-                                        bool include_quota_nodes);
+  // DEPRECATED(crbug.com/1271155): The cookies tree model is slowly being
+  // deprecated, during this process the semantics of the model are nuanced
+  // w.r.t partitioned storage, and should not be used in new locations.
+  base::Value::List GetChildNodeDetailsDeprecated(const CookieTreeNode* parent);
 
   // Gets tree node from |path| under |root|. |path| is comma separated list of
   // ids. |id_map| translates ids into object pointers. Return NULL if |path|
@@ -51,8 +53,7 @@
   // maps a CookieTreeNode to an ID and creates a new ID if `node` is not in the
   // maps. Returns nullopt if the `node` does not need to be shown.
   absl::optional<base::Value::Dict> GetCookieTreeNodeDictionary(
-      const CookieTreeNode& node,
-      bool include_quota_nodes);
+      const CookieTreeNode& node);
 
   // IDMap to create unique ID and look up the object for an ID.
   CookiesTreeNodeIdMap id_map_;
diff --git a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
index 9e1c2c1e..d3f1f286 100644
--- a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
@@ -78,6 +78,9 @@
        IDS_SETTINGS_COOKIES_MEDIA_LICENSE},
       {CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE,
        IDS_SETTINGS_COOKIES_MEDIA_LICENSE},
+
+      {CookieTreeNode::DetailedInfo::TYPE_QUOTA,
+       IDS_SETTINGS_COOKIES_QUOTA_STORAGE},
   };
   // Before optimizing, consider the data size and the cost of L2 cache misses.
   // A linear search over a couple dozen integers is very fast.
@@ -225,10 +228,10 @@
 void CookiesViewHandler::RecreateCookiesTreeModel() {
   cookies_tree_model_.reset();
   filter_.clear();
-  cookies_tree_model_ =
-      cookies_tree_model_for_testing_.get()
-          ? std::move(cookies_tree_model_for_testing_)
-          : CookiesTreeModel::CreateForProfile(Profile::FromWebUI(web_ui()));
+  cookies_tree_model_ = cookies_tree_model_for_testing_.get()
+                            ? std::move(cookies_tree_model_for_testing_)
+                            : CookiesTreeModel::CreateForProfileDeprecated(
+                                  Profile::FromWebUI(web_ui()));
   cookies_tree_model_->AddCookiesTreeObserver(this);
 }
 
@@ -255,10 +258,7 @@
     return;
   }
 
-  // TODO (crbug.com/642955): Pass true for `include_quota_nodes` parameter
-  // when quota nodes include local/session storage in the total.
-  base::Value::List children =
-      model_util_->GetChildNodeDetails(node, /* include_quota_nodes */ false);
+  base::Value::List children = model_util_->GetChildNodeDetailsDeprecated(node);
 
   ResolveJavascriptCallback(base::Value(callback_id),
                             base::Value(std::move(children)));
@@ -440,14 +440,12 @@
   for (const auto& site : parent->children()) {
     std::u16string description;
     for (const auto& category : site->children()) {
+      const auto node_type = category->GetDetailedInfo().node_type;
       if (!description.empty())
         description += u", ";
-      const auto node_type = category->GetDetailedInfo().node_type;
       size_t item_count = category->children().size();
+
       switch (node_type) {
-        case CookieTreeNode::DetailedInfo::TYPE_QUOTA:
-          // TODO(crbug.com/642955): Omit quota values until bug is addressed.
-          continue;
         case CookieTreeNode::DetailedInfo::TYPE_COOKIE:
           DCHECK_EQ(0u, item_count);
           item_count = 1;
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index be0d0a40..77d127b 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2041,6 +2041,7 @@
     {"cookieMediaLicense", IDS_SETTINGS_COOKIES_MEDIA_LICENSE},
     {"cookieServiceWorker", IDS_SETTINGS_COOKIES_SERVICE_WORKER},
     {"cookieSharedWorker", IDS_SETTINGS_COOKIES_SHARED_WORKER},
+    {"cookieQuotaStorage", IDS_SETTINGS_COOKIES_QUOTA_STORAGE},
     {"embeddedOnAnyHost", IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_ANY_HOST},
     {"embeddedOnHost", IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_HOST},
     {"editSiteTitle", IDS_SETTINGS_EDIT_SITE_TITLE},
@@ -2071,6 +2072,8 @@
      IDS_SETTINGS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
     {"localStorageOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
     {"localStorageSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+    {"quotaOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+    {"quotaSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
     {"mediaLicenseOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
     {"mediaLicenseSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
     {"mediaLicenseLastModified",
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index d315f39..9bbfd03 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -1601,7 +1601,7 @@
 void SiteSettingsHandler::EnsureCookiesTreeModelCreated() {
   if (cookies_tree_model_)
     return;
-  cookies_tree_model_ = CookiesTreeModel::CreateForProfile(profile_);
+  cookies_tree_model_ = CookiesTreeModel::CreateForProfileDeprecated(profile_);
   cookies_tree_model_->AddCookiesTreeObserver(this);
 }
 
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index a0839d13..f9e3f3d 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/signin_modal_dialog.h"
 #include "chrome/browser/ui/signin_view_controller_delegate.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
@@ -377,6 +378,28 @@
 #endif
   }
 
+  static bool TryCompleteProfileCustomizationDialog(Browser* browser) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    NOTREACHED();
+    return false;
+#else
+    SigninViewController* signin_view_controller =
+        browser->signin_view_controller();
+    DCHECK(signin_view_controller);
+    if (!signin_view_controller->ShowsModalDialog())
+      return false;
+    content::WebContents* dialog_web_contents =
+        signin_view_controller->GetModalDialogWebContentsForTesting();
+    DCHECK(dialog_web_contents);
+    std::string button_selector =
+        GetButtonSelectorForApp("profile-customization-app", "doneButton");
+    if (!IsElementReady(dialog_web_contents, button_selector))
+      return false;
+
+    return content::ExecJs(dialog_web_contents, button_selector + ".click();");
+#endif
+  }
+
   static bool ShowsModalDialog(Browser* browser) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     NOTREACHED();
@@ -485,6 +508,17 @@
 #endif
 }
 
+bool TryUntilSuccessWithTimeout(base::RepeatingCallback<bool()> try_callback,
+                                base::TimeDelta timeout) {
+  const base::Time expire_time = base::Time::Now() + timeout;
+  while (base::Time::Now() <= expire_time) {
+    if (try_callback.Run())
+      return true;
+    RunLoopFor(base::Seconds(1));
+  }
+  return false;
+}
+
 bool DismissSyncConfirmationDialog(Browser* browser,
                                    base::TimeDelta timeout,
                                    SyncConfirmationDialogAction action) {
@@ -517,28 +551,21 @@
     Browser* browser,
     base::TimeDelta timeout,
     SigninEmailConfirmationDialog::Action action) {
-  const base::Time expire_time = base::Time::Now() + timeout;
-  while (base::Time::Now() <= expire_time) {
-    if (SigninViewControllerTestUtil::TryCompleteSigninEmailConfirmationDialog(
-            browser, action)) {
-      return true;
-    }
-    RunLoopFor(base::Milliseconds(1000));
-  }
-  return false;
+  return TryUntilSuccessWithTimeout(
+      base::BindRepeating(SigninViewControllerTestUtil::
+                              TryCompleteSigninEmailConfirmationDialog,
+                          browser, action),
+      timeout);
 }
 
 bool CompleteReauthConfirmationDialog(Browser* browser,
                                       base::TimeDelta timeout,
                                       ReauthDialogAction action) {
-  const base::Time expire_time = base::Time::Now() + timeout;
-  while (base::Time::Now() <= expire_time) {
-    if (SigninViewControllerTestUtil::TryCompleteReauthConfirmationDialog(
-            browser, action))
-      return true;
-    RunLoopFor(base::Milliseconds(1000));
-  }
-  return false;
+  return TryUntilSuccessWithTimeout(
+      base::BindRepeating(
+          SigninViewControllerTestUtil::TryCompleteReauthConfirmationDialog,
+          browser, action),
+      timeout);
 }
 
 bool ConfirmReauthConfirmationDialog(Browser* browser,
@@ -552,4 +579,13 @@
                                           ReauthDialogAction::kCancel);
 }
 
+bool CompleteProfileCustomizationDialog(Browser* browser,
+                                        base::TimeDelta timeout) {
+  return TryUntilSuccessWithTimeout(
+      base::BindRepeating(
+          SigninViewControllerTestUtil::TryCompleteProfileCustomizationDialog,
+          browser),
+      timeout);
+}
+
 }  // namespace login_ui_test_utils
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.h b/chrome/browser/ui/webui/signin/login_ui_test_utils.h
index a82ddc9..64fd97b 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.h
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.h
@@ -76,6 +76,13 @@
 // dismissed before |timeout|.
 bool CancelReauthConfirmationDialog(Browser* browser, base::TimeDelta timeout);
 
+// Waits for profile customization dialog to get displayed, then executes
+// javascript to click on done button. Returns false if dialog wasn't
+// dismissed before |timeout|.
+bool CompleteProfileCustomizationDialog(
+    Browser* browser,
+    base::TimeDelta timeout = kSyncConfirmationDialogTimeout);
+
 }  // namespace login_ui_test_utils
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_TEST_UTILS_H_
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index d7aeb9b..0f75b455 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1656309201-2b13d3cf1b16e73aaee6f1b01c723a408a4ed23b.profdata
+chrome-linux-main-1656331133-e188c75043fb172bb25e56585a65bfb4d555a028.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 4d78784d..5f0bdca3 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1656309201-289a9bda1677eb58736522a08931b2c74e68825f.profdata
+chrome-mac-arm-main-1656331133-1941bc675dfd506e6053d4ab5ca07fd9350ca513.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 91c5390..e2f55039 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1656309201-f5c959e5265215e93b5e4a6a79db46f37b70ee15.profdata
+chrome-mac-main-1656331133-2a720bfcc444ff205f61e877781c27bf146bd128.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 7b655c4..0eae1728 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1656298739-279d83ba0e899c736f5992f56d03cafc793abf88.profdata
+chrome-win32-main-1656331133-a87a490e3491ee6b22dae312d1d5347435280cba.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 7816bc05..e9c6e43 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1656309201-9b8462d941fc60dde6d09d3949c8ccc837846ef8.profdata
+chrome-win64-main-1656331133-4becbbd6a2c3171b7cf4baeaf684180260bd6617.profdata
diff --git a/chrome/credential_provider/gaiacp/strings/gaia_resources_my.xtb b/chrome/credential_provider/gaiacp/strings/gaia_resources_my.xtb
index 0cf0121..fc7eb723 100644
--- a/chrome/credential_provider/gaiacp/strings/gaia_resources_my.xtb
+++ b/chrome/credential_provider/gaiacp/strings/gaia_resources_my.xtb
@@ -7,7 +7,7 @@
 <translation id="2515346402363002066">သတ်မှတ်ပေးထားသည့်အချိန် ကုန်သွားပါပြီ။ သင့်အလုပ်သုံးအကောင့်နှင့် လက်မှတ်ထိုးဝင်ပါ။</translation>
 <translation id="2549902055700841962">သင့်အလုပ်သုံး အကောင့်သို့ လက်မှတ်ထိုးဝင်၍ မရပါ။ သင့်စီမံခန့်ခွဲသူထံ ဆက်သွယ်ပါ။</translation>
 <translation id="2566603360883977759">ဤအီးမေးလ်ဖြင့် လက်မှတ်ထိုးဝင်ခွင့်မပြုပါ။ အလုပ် (သို့) ကျောင်းအတွက် သင်အသုံးပြုသောအကောင့်ကို ပြန်စမ်းကြည့်ပါ။ လက်မှတ်ထိုးဝင်၍မရသေးပါက သင်၏စီမံခန့်ခွဲသူထံ ဆက်သွယ်ပါ။</translation>
-<translation id="2844349213149998955">ဤစက်ပေါ်တွင် ကိုယ်ရေးကိုယ်တာအကောင့်နှင့် လက်မှတ်ထိုးဝင်ခြင်းကို ခွင့်မပြုပါ။ အလုပ်သုံး အကောင့်နှင့် လက်မှတ်ထိုးဝင်ပါ။</translation>
+<translation id="2844349213149998955">ဤစက်ပေါ်တွင် ကိုယ်ပိုင်အကောင့်ဖြင့် လက်မှတ်ထိုးဝင်ခြင်းကို ခွင့်မပြုပါ။ အလုပ်သုံး အကောင့်ဖြင့် ဝင်ပါ။</translation>
 <translation id="3217145568844727893">သင့်လက်ရှိ Windows စကားဝှက် မထည့်ဘဲ ရှေ့ဆက်ပါက ဤစက်ပေါ်ရှိဒေတာများ အပြီးဆုံးရှုံးသွားနိုင်သည်။</translation>
 <translation id="3306357053520292004">ဤကွန်ပျူတာပေါ်ရှိ အသုံးပြုသူတစ်ဦးကို ဤအကောင့်သုံး၍ ထည့်ထားပြီး ဖြစ်ပါသည်။ အခြားအကောင့်တစ်ခုဖြင့် လက်မှတ်ထိုးဝင်ရောက်ပါ။</translation>
 <translation id="3355053591933237049">သင့်စက်ကို အင်တာနက်ချိတ်ဆက်ထားခြင်း ရှိမရှိ စစ်ဆေးပြီး ထပ်စမ်းကြည့်ပါ</translation>
diff --git a/chrome/renderer/resources/extensions/remote_apps/OWNERS b/chrome/renderer/resources/extensions/remote_apps/OWNERS
index aef9c2c4..24cfb61 100644
--- a/chrome/renderer/resources/extensions/remote_apps/OWNERS
+++ b/chrome/renderer/resources/extensions/remote_apps/OWNERS
@@ -1 +1,2 @@
 hendrich@chromium.org
+mpetrisor@chromium.org
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 93bae7f..cb667d6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1670,6 +1670,7 @@
       "../browser/browsing_data/browsing_data_remover_browsertest_base.cc",
       "../browser/browsing_data/browsing_data_remover_browsertest_base.h",
       "../browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc",
+      "../browser/browsing_data/cookies_tree_model_browsertest.cc",
       "../browser/browsing_data/counters/autofill_counter_browsertest.cc",
       "../browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc",
       "../browser/browsing_data/counters/cache_counter_browsertest.cc",
@@ -2479,7 +2480,6 @@
         "../browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc",
         "../browser/ui/views/chrome_cleaner_reboot_dialog_browsertest_win.cc",
         "../browser/ui/views/settings_reset_prompt_dialog_browsertest.cc",
-        "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc",
         "../browser/ui/views/uninstall_view_browsertest.cc",
         "../test/delayload/delayload_hook_browsertest.cc",
       ]
@@ -5169,6 +5169,7 @@
     "../browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/document_write_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc",
+    "../browser/page_load_metrics/observers/gws_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc",
@@ -9364,6 +9365,7 @@
           "../browser/ui/views/accessibility/post_install_announcement_browsertest.cc",
           "../browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc",
           "../browser/ui/views/status_icons/status_tray_state_changer_interactive_uitest_win.cc",
+          "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc",
         ]
         if (use_aura) {
           sources += [ "../browser/ui/views/tooltip/tooltip_aura_interactive_uitest_win.cc" ]
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/c.html b/chrome/test/data/extensions/api_test/webnavigation/getFrame/c.html
deleted file mode 100644
index d1220cf2..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/c.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-    <body>
-        <iframe src="a.html"></iframe>
-    </body>
-</html>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/prerender.html b/chrome/test/data/extensions/api_test/webnavigation/getFrame/prerender.html
deleted file mode 100644
index 7590241..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/prerender.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script type="speculationrules">
-{
-  "prerender":[
-    {"source": "list",
-     "urls": ["a.html"]}
-  ]
-}
-</script>
-<title>Prerender Test</title>
-</head>
-<body><a href="a.html" id="link">link</a></body>
-</html>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/prerender_multipleframes.html b/chrome/test/data/extensions/api_test/webnavigation/getFrame/prerender_multipleframes.html
deleted file mode 100644
index 5bd883ef..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/prerender_multipleframes.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script type="speculationrules">
-{
-  "prerender":[
-    {"source": "list",
-     "urls": ["c.html"]}
-  ]
-}
-</script>
-<title>Prerender Test</title>
-</head>
-<body><a href="c.html" id="link">link</a></body>
-</html>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
index f292f2f..61cd3d3 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
@@ -22,8 +22,6 @@
 ready.then(async function() {
   var URL = chrome.extension.getURL("a.html");
   var URL_FRAMES = chrome.extension.getURL("b.html");
-  let config = await promise(chrome.test.getConfig);
-  let port = config.testServer.port;
   var processId = -1;
   var documentId;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
@@ -149,142 +147,7 @@
           chrome.test.succeed();
       });
     },
-    async function testGetPrerenderingFrames() {
-      const urlPrefix =
-      `http://a.test:${port}/extensions/api_test/webnavigation/getFrame/`;
-      const initialUrl = urlPrefix + "a.html?initial";
-      const prerenderTargetUrl = urlPrefix + "a.html";
-      const initiatorUrl = urlPrefix + "prerender.html";
 
-      let tab = await promise(chrome.tabs.create, {"url": initialUrl});
-
-      var done = chrome.test.listenForever(
-        chrome.webNavigation.onCommitted,
-        function (details) {
-          // Ignore frames other than the pre-rendered frame.
-          if (details.tabId != tab.id || details.url != prerenderTargetUrl)
-            return;
-          // prerendered main frame shouldn't have frameId = 0.
-          if (details.frameId == 0)
-            return;
-          chrome.webNavigation.getAllFrames(
-              {tabId: tab.id},
-            function (frameDetails) {
-              chrome.test.assertEq(
-                  [{errorOccurred: false,
-                    frameId: details.frameId,
-                    parentFrameId: -1,
-                    processId: details.processId,
-                    url: prerenderTargetUrl,
-                    documentId: details.documentId,
-                    documentLifecycle: "prerender",
-                    frameType: "outermost_frame"}],
-                    frameDetails.filter(ob => ob.url === prerenderTargetUrl));
-              done();
-          });
-      });
-
-      // TODO(crbug/1273341): Modify the testcase to be triggering concurrent
-      // multiple prerendering pages once it is supported.
-      // Navigate to a page that initiates prerendering "a.html".
-      chrome.tabs.update(tab.id, {"url": initiatorUrl});
-    },
-    async function testGetPrerenderingFramesAndSubframes() {
-      const urlPrefix =
-      `http://a.test:${port}/extensions/api_test/webnavigation/getFrame/`;
-      const initialUrl = urlPrefix + "a.html?initial";
-      const prerenderTargetUrl = urlPrefix + "c.html";
-      const prerenderTargetSubframeUrl = urlPrefix + "a.html";
-      const initiatorUrl = urlPrefix + "prerender_multipleframes.html";
-
-      let tab = await promise(chrome.tabs.create, {"url": initialUrl});
-
-      var done = chrome.test.listenForever(
-        chrome.webNavigation.onCommitted,
-        function (details) {
-          // Ignore frames other than the pre-rendered subframe to ensure all
-          // frames are loaded.
-          if (details.tabId != tab.id ||
-              details.url != prerenderTargetSubframeUrl)
-            return;
-
-          // A prerendered subframe is expected to have a parent.
-          if (details.parentFrameId == -1)
-            return;
-
-          chrome.webNavigation.getAllFrames(
-              {tabId: tab.id},
-            function (frameDetails) {
-              chrome.test.assertEq(
-                  [{errorOccurred: false,
-                    frameId: details.parentFrameId,
-                    parentFrameId: -1,
-                    processId: details.processId,
-                    url: prerenderTargetUrl,
-                    documentId: details.parentDocumentId,
-                    documentLifecycle: "prerender",
-                    frameType: "outermost_frame"},
-                    {errorOccurred: false,
-                    frameId: details.frameId,
-                    parentFrameId: details.parentFrameId,
-                    processId: details.processId,
-                    url: prerenderTargetSubframeUrl,
-                    documentId: details.documentId,
-                    parentDocumentId: details.parentDocumentId,
-                    documentLifecycle: "prerender",
-                    frameType: "sub_frame"}],
-                    frameDetails.filter(ob => ob.documentLifecycle ===
-                      "prerender"));
-              done();
-          });
-      });
-
-      // Navigate to a page that initiates prerendering "c.html", which contains
-      // a subframe "a.html".
-      chrome.tabs.update(tab.id, {"url": initiatorUrl});
-    },
-    async function testGetActivatedPrerenderingFrames() {
-      const urlPrefix =
-      `http://a.test:${port}/extensions/api_test/webnavigation/getFrame/`;
-      const initialUrl = urlPrefix + "a.html?initial";
-      const prerenderTargetUrl = urlPrefix + "a.html";
-      const initiatorUrl = urlPrefix + "prerender.html";
-
-      let tab = await promise(chrome.tabs.create, {"url": initialUrl});
-
-      var done = chrome.test.listenForever(
-        chrome.webNavigation.onCommitted,
-        function (details) {
-          // Ignore frames other than the pre-rendered frame.
-          if (details.tabId != tab.id || details.url != prerenderTargetUrl)
-            return;
-
-          // Trigger prerendered frame activation upon the first navigation.
-          if (details.documentLifecycle === 'prerender') {
-            chrome.tabs.update(tab.id, {"url": prerenderTargetUrl});
-            return;
-          }
-
-          chrome.webNavigation.getAllFrames(
-              {tabId: tab.id},
-            function (frameDetails) {
-              chrome.test.assertEq(
-                  [{errorOccurred: false,
-                    frameId: 0,
-                    parentFrameId: -1,
-                    processId: details.processId,
-                    url: prerenderTargetUrl,
-                    documentId: details.documentId,
-                    documentLifecycle: "active",
-                    frameType: "outermost_frame"}],
-                    frameDetails.filter(ob => ob.url === prerenderTargetUrl));
-              done();
-          });
-      });
-
-      // Navigate to a page that initiates prerendering "a.html".
-      chrome.tabs.update(tab.id, {"url": initiatorUrl});
-    },
     // Load an URL with a frame which is detached during load.
     // getAllFrames should only return the remaining (main) frame.
     async function testFrameDetach() {
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
index 8009abd3..a012165 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
@@ -8,6 +8,7 @@
 import {AdditionalContextQueryParam, FeedbackFlowElement, FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
 import {FeedbackContext, SendReportStatus} from 'chrome://os-feedback/feedback_types.js';
 import {setFeedbackServiceProviderForTesting, setHelpContentProviderForTesting} from 'chrome://os-feedback/mojo_interface_provider.js';
+import {SearchPageElement} from 'chrome://os-feedback/search_page.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
@@ -64,6 +65,13 @@
     return page.feedbackContext_;
   }
 
+  /** @return {!SearchPageElement} */
+  function getSearchPage() {
+    assertTrue(!!page);
+
+    return /** @type {!SearchPageElement} */ (page.$['searchPage']);
+  }
+
   // Test that the search page is shown by default.
   test('SearchPageIsShownByDefault', async () => {
     await initializePage();
@@ -348,9 +356,15 @@
         const extra_diagnostics = 'some%20extra%20diagnostics';
         queryParams.set(
             AdditionalContextQueryParam.EXTRA_DIAGNOSTICS, extra_diagnostics);
+        const description_template = 'Q1%3A%20Question%20one?';
+        queryParams.set(
+            AdditionalContextQueryParam.DESCRIPTION_TEMPLATE,
+            description_template);
         // Replace current querystring with the new one.
         window.history.replaceState(null, '', '?' + queryParams.toString());
         await initializePage();
+        page.setCurrentStateForTesting(FeedbackFlowState.SEARCH);
+        const descriptionElement = getSearchPage().$['descriptionText'];
 
         const feedbackContext = getFeedbackContext_();
         assertEquals(fakeFeedbackContext.pageUrl, feedbackContext.pageUrl);
@@ -358,6 +372,8 @@
         assertEquals(
             decodeURIComponent(extra_diagnostics),
             feedbackContext.extraDiagnostics);
+        assertEquals(
+            decodeURIComponent(description_template), descriptionElement.value);
       });
 
   // Test that the extra diagnostics gets set when query parameter is empty.
@@ -370,11 +386,14 @@
             '?' +
                 '');
         await initializePage();
+        page.setCurrentStateForTesting(FeedbackFlowState.SEARCH);
+        const descriptionElement = getSearchPage().$['descriptionText'];
 
         const feedbackContext = getFeedbackContext_();
         // TODO(ashleydp): Update expectation when page_url passed.
         assertEquals(fakeFeedbackContext.pageUrl, feedbackContext.pageUrl);
         assertEquals(fakeFeedbackContext.email, feedbackContext.email);
         assertEquals('', feedbackContext.extraDiagnostics);
+        assertEquals('', descriptionElement.value);
       });
 }
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 0a323b22..eb1df16e 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -489,11 +489,29 @@
   }
 };
 
-// TODO(crbug.com/1307443): disabling due to flakiness on several builders.
+TEST_F('CrSettingsPrivacyGuidePageTest', 'PrivacyGuidePageTests', function() {
+  runMochaSuite('PrivacyGuidePageTests');
+});
+
+TEST_F('CrSettingsPrivacyGuidePageTest', 'MsbbFragmentNavigations', function() {
+  runMochaSuite('MsbbFragmentNavigations');
+});
+
 TEST_F(
-    'CrSettingsPrivacyGuidePageTest', 'DISABLED_PrivacyGuidePageTests',
+    'CrSettingsPrivacyGuidePageTest', 'HistorySyncFragmentNavigations',
     function() {
-        runMochaSuite('PrivacyGuidePage');
+      runMochaSuite('HistorySyncFragmentNavigations');
+    });
+
+TEST_F(
+    'CrSettingsPrivacyGuidePageTest', 'SafeBrowsingFragmentNavigations',
+    function() {
+      runMochaSuite('SafeBrowsingFragmentNavigations');
+    });
+
+TEST_F(
+    'CrSettingsPrivacyGuidePageTest', 'CookiesFragmentNavigations', function() {
+      runMochaSuite('CookiesFragmentNavigations');
     });
 
 // TODO(crbug.com/1328037): Flaky on Linux Tests(dbg).
diff --git a/chrome/test/data/webui/settings/privacy_guide_page_test.ts b/chrome/test/data/webui/settings/privacy_guide_page_test.ts
index d1f2bfc..f12f6863f 100644
--- a/chrome/test/data/webui/settings/privacy_guide_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_page_test.ts
@@ -105,7 +105,239 @@
   return promise;
 }
 
-suite('PrivacyGuidePage', function() {
+// Set the cookies setting for the privacy guide.
+function setCookieSetting(
+    page: SettingsPrivacyGuidePageElement, setting: CookiePrimarySetting) {
+  page.set('prefs.generated.cookie_primary_setting', {
+    type: chrome.settingsPrivate.PrefType.NUMBER,
+    value: setting,
+  });
+}
+
+function shouldShowCookiesCard(page: SettingsPrivacyGuidePageElement): boolean {
+  const setting = page.getPref('generated.cookie_primary_setting').value;
+  return setting === CookiePrimarySetting.BLOCK_THIRD_PARTY ||
+      setting === CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO;
+}
+
+// Set the safe browsing setting for the privacy guide.
+function setSafeBrowsingSetting(
+    page: SettingsPrivacyGuidePageElement, setting: SafeBrowsingSetting) {
+  page.set('prefs.generated.safe_browsing', {
+    type: chrome.settingsPrivate.PrefType.NUMBER,
+    value: setting,
+  });
+}
+
+function shouldShowSafeBrowsingCard(page: SettingsPrivacyGuidePageElement):
+    boolean {
+  const setting = page.getPref('generated.safe_browsing').value;
+  return setting === SafeBrowsingSetting.ENHANCED ||
+      setting === SafeBrowsingSetting.STANDARD;
+}
+
+function assertQueryParameter(step: PrivacyGuideStep) {
+  assertEquals(step, Router.getInstance().getQueryParameters().get('step'));
+}
+
+function shouldShowHistorySyncCard(syncBrowserProxy: TestSyncBrowserProxy):
+    boolean {
+  return !syncBrowserProxy.testSyncStatus ||
+      !!syncBrowserProxy.testSyncStatus.signedIn;
+}
+
+type AssertCardComponentsVisibleParams = {
+  page: SettingsPrivacyGuidePageElement,
+  isSettingFooterVisibleExpected?: boolean,
+  isBackButtonVisibleExpected?: boolean,
+  isWelcomeFragmentVisibleExpected?: boolean,
+  isCompletionFragmentVisibleExpected?: boolean,
+  isMsbbFragmentVisibleExpected?: boolean,
+  isClearOnExitFragmentVisibleExpected?: boolean,
+  isHistorySyncFragmentVisibleExpected?: boolean,
+  isSafeBrowsingFragmentVisibleExpected?: boolean,
+  isCookiesFragmentVisibleExpected?: boolean,
+};
+
+function assertCardComponentsVisible({
+  page,
+  isSettingFooterVisibleExpected,
+  isBackButtonVisibleExpected,
+  isWelcomeFragmentVisibleExpected,
+  isCompletionFragmentVisibleExpected,
+  isMsbbFragmentVisibleExpected,
+  isClearOnExitFragmentVisibleExpected,
+  isHistorySyncFragmentVisibleExpected,
+  isSafeBrowsingFragmentVisibleExpected,
+  isCookiesFragmentVisibleExpected,
+}: AssertCardComponentsVisibleParams) {
+  assertEquals(
+      !!isSettingFooterVisibleExpected, isChildVisible(page, '#settingFooter'));
+  if (isSettingFooterVisibleExpected) {
+    const backButtonVisibility =
+        getComputedStyle(
+            page.shadowRoot!.querySelector<HTMLElement>('#backButton')!)
+            .visibility;
+    assertEquals(
+        isBackButtonVisibleExpected ? 'visible' : 'hidden',
+        backButtonVisibility);
+  }
+  assertEquals(
+      !!isWelcomeFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.WELCOME));
+  assertEquals(
+      !!isCompletionFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.COMPLETION));
+  assertEquals(
+      !!isMsbbFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.MSBB));
+  assertEquals(
+      !!isClearOnExitFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.CLEAR_ON_EXIT));
+  assertEquals(
+      !!isHistorySyncFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.HISTORY_SYNC));
+  assertEquals(
+      !!isSafeBrowsingFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.SAFE_BROWSING));
+  assertEquals(
+      !!isCookiesFragmentVisibleExpected,
+      isChildVisible(page, '#' + PrivacyGuideStep.COOKIES));
+}
+
+/**
+ * @return The expected total number of active cards for the step indicator.
+ */
+function getExpectedNumberOfActiveCards(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy) {
+  let numSteps = PRIVACY_GUIDE_STEPS;
+  if (!shouldShowHistorySyncCard(syncBrowserProxy)) {
+    numSteps -= 1;
+  }
+  if (!shouldShowCookiesCard(page)) {
+    numSteps -= 1;
+  }
+  if (!shouldShowSafeBrowsingCard(page)) {
+    numSteps -= 1;
+  }
+  return numSteps;
+}
+
+function assertStepIndicatorModel(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy, activeIndex: number) {
+  const model = page.computeStepIndicatorModel();
+  assertEquals(activeIndex, model.active);
+  assertEquals(
+      getExpectedNumberOfActiveCards(page, syncBrowserProxy), model.total);
+}
+
+function assertWelcomeCardVisible(page: SettingsPrivacyGuidePageElement) {
+  assertQueryParameter(PrivacyGuideStep.WELCOME);
+  assertCardComponentsVisible({
+    page: page,
+    isWelcomeFragmentVisibleExpected: true,
+  });
+}
+
+function assertCompletionCardVisible(page: SettingsPrivacyGuidePageElement) {
+  assertQueryParameter(PrivacyGuideStep.COMPLETION);
+  assertCardComponentsVisible({
+    page: page,
+    isCompletionFragmentVisibleExpected: true,
+  });
+}
+
+function assertMsbbCardVisible(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy) {
+  assertQueryParameter(PrivacyGuideStep.MSBB);
+  assertCardComponentsVisible({
+    page: page,
+    isSettingFooterVisibleExpected: true,
+    isBackButtonVisibleExpected: true,
+    isMsbbFragmentVisibleExpected: true,
+  });
+  assertStepIndicatorModel(page, syncBrowserProxy, 0);
+}
+
+function assertHistorySyncCardVisible(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy) {
+  assertQueryParameter(PrivacyGuideStep.HISTORY_SYNC);
+  assertCardComponentsVisible({
+    page: page,
+    isSettingFooterVisibleExpected: true,
+    isBackButtonVisibleExpected: true,
+    isHistorySyncFragmentVisibleExpected: true,
+  });
+  assertStepIndicatorModel(page, syncBrowserProxy, 1);
+}
+
+function assertSafeBrowsingCardVisible(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy) {
+  assertQueryParameter(PrivacyGuideStep.SAFE_BROWSING);
+  assertCardComponentsVisible({
+    page: page,
+    isSettingFooterVisibleExpected: true,
+    isBackButtonVisibleExpected: true,
+    isSafeBrowsingFragmentVisibleExpected: true,
+  });
+  assertStepIndicatorModel(
+      page, syncBrowserProxy,
+      shouldShowHistorySyncCard(syncBrowserProxy) ? 2 : 1);
+}
+
+function assertCookiesCardVisible(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy) {
+  assertQueryParameter(PrivacyGuideStep.COOKIES);
+  assertCardComponentsVisible({
+    page: page,
+    isSettingFooterVisibleExpected: true,
+    isBackButtonVisibleExpected: true,
+    isCookiesFragmentVisibleExpected: true,
+  });
+  let activeIndex = 3;
+  if (!shouldShowHistorySyncCard(syncBrowserProxy)) {
+    activeIndex -= 1;
+  }
+  if (!shouldShowSafeBrowsingCard(page)) {
+    activeIndex -= 1;
+  }
+  assertStepIndicatorModel(page, syncBrowserProxy, activeIndex);
+}
+
+// Bundles functionality to create the page object for tests.
+function createPrivacyGuidePageForTest(settingsPrefs: SettingsPrefsElement) {
+  document.body.innerHTML = '';
+  const page = document.createElement('settings-privacy-guide-page');
+  page.disableAnimationsForTesting();
+  page.prefs = settingsPrefs.prefs!;
+  document.body.appendChild(page);
+
+  setupPrivacyRouteForTest();
+
+  return page;
+}
+
+// Bundles frequently used functionality to configure the page object for tests.
+function setupPrivacyGuidePageForTest(
+    page: SettingsPrivacyGuidePageElement,
+    syncBrowserProxy: TestSyncBrowserProxy) {
+  setSafeBrowsingSetting(page, SafeBrowsingSetting.STANDARD);
+  setCookieSetting(page, CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO);
+  setupSync({
+    syncBrowserProxy: syncBrowserProxy,
+    syncOn: true,
+    syncAllDataTypes: true,
+    typedUrlsSynced: true,
+  });
+}
+
+suite('PrivacyGuidePageTests', function() {
   let page: SettingsPrivacyGuidePageElement;
   let settingsPrefs: SettingsPrefsElement;
   let syncBrowserProxy: TestSyncBrowserProxy;
@@ -123,22 +355,8 @@
     syncBrowserProxy.testSyncStatus = null;
     SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
 
-    document.body.innerHTML = '';
-    page = document.createElement('settings-privacy-guide-page');
-    page.disableAnimationsForTesting();
-    page.prefs = settingsPrefs.prefs!;
-    document.body.appendChild(page);
-
-    setSafeBrowsingSetting(SafeBrowsingSetting.STANDARD);
-    setCookieSetting(CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO);
-    setupSync({
-      syncBrowserProxy: syncBrowserProxy,
-      syncOn: true,
-      syncAllDataTypes: true,
-      typedUrlsSynced: true,
-    });
-
-    setupPrivacyRouteForTest();
+    page = createPrivacyGuidePageForTest(settingsPrefs);
+    setupPrivacyGuidePageForTest(page, syncBrowserProxy);
 
     return flushTasks();
   });
@@ -150,189 +368,6 @@
     Router.getInstance().navigateTo(routes.BASIC);
   });
 
-  function assertQueryParameter(step: PrivacyGuideStep) {
-    assertEquals(step, Router.getInstance().getQueryParameters().get('step'));
-  }
-
-  function shouldShowHistorySyncCard(): boolean {
-    return !syncBrowserProxy.testSyncStatus ||
-        !!syncBrowserProxy.testSyncStatus.signedIn;
-  }
-
-  /**
-   * Set the cookies setting for the privacy guide.
-   */
-  function setCookieSetting(setting: CookiePrimarySetting) {
-    page.set('prefs.generated.cookie_primary_setting', {
-      type: chrome.settingsPrivate.PrefType.NUMBER,
-      value: setting,
-    });
-  }
-
-  function shouldShowCookiesCard(): boolean {
-    const setting = page.getPref('generated.cookie_primary_setting').value;
-    return setting === CookiePrimarySetting.BLOCK_THIRD_PARTY ||
-        setting === CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO;
-  }
-
-  /**
-   * Set the safe browsing setting for the privacy guide.
-   */
-  function setSafeBrowsingSetting(setting: SafeBrowsingSetting) {
-    page.set('prefs.generated.safe_browsing', {
-      type: chrome.settingsPrivate.PrefType.NUMBER,
-      value: setting,
-    });
-  }
-
-  function shouldShowSafeBrowsingCard(): boolean {
-    const setting = page.getPref('generated.safe_browsing').value;
-    return setting === SafeBrowsingSetting.ENHANCED ||
-        setting === SafeBrowsingSetting.STANDARD;
-  }
-
-  type AssertCardComponentsVisibleParams = {
-    isSettingFooterVisibleExpected?: boolean,
-    isBackButtonVisibleExpected?: boolean,
-    isWelcomeFragmentVisibleExpected?: boolean,
-    isCompletionFragmentVisibleExpected?: boolean,
-    isMsbbFragmentVisibleExpected?: boolean,
-    isClearOnExitFragmentVisibleExpected?: boolean,
-    isHistorySyncFragmentVisibleExpected?: boolean,
-    isSafeBrowsingFragmentVisibleExpected?: boolean,
-    isCookiesFragmentVisibleExpected?: boolean,
-  };
-
-  function assertCardComponentsVisible({
-    isSettingFooterVisibleExpected,
-    isBackButtonVisibleExpected,
-    isWelcomeFragmentVisibleExpected,
-    isCompletionFragmentVisibleExpected,
-    isMsbbFragmentVisibleExpected,
-    isClearOnExitFragmentVisibleExpected,
-    isHistorySyncFragmentVisibleExpected,
-    isSafeBrowsingFragmentVisibleExpected,
-    isCookiesFragmentVisibleExpected,
-  }: AssertCardComponentsVisibleParams) {
-    assertEquals(
-        !!isSettingFooterVisibleExpected,
-        isChildVisible(page, '#settingFooter'));
-    if (isSettingFooterVisibleExpected) {
-      const backButtonVisibility =
-          getComputedStyle(
-              page.shadowRoot!.querySelector<HTMLElement>('#backButton')!)
-              .visibility;
-      assertEquals(
-          isBackButtonVisibleExpected ? 'visible' : 'hidden',
-          backButtonVisibility);
-    }
-    assertEquals(
-        !!isWelcomeFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.WELCOME));
-    assertEquals(
-        !!isCompletionFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.COMPLETION));
-    assertEquals(
-        !!isMsbbFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.MSBB));
-    assertEquals(
-        !!isClearOnExitFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.CLEAR_ON_EXIT));
-    assertEquals(
-        !!isHistorySyncFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.HISTORY_SYNC));
-    assertEquals(
-        !!isSafeBrowsingFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.SAFE_BROWSING));
-    assertEquals(
-        !!isCookiesFragmentVisibleExpected,
-        isChildVisible(page, '#' + PrivacyGuideStep.COOKIES));
-  }
-
-  /**
-   * @return The expected total number of active cards for the step indicator.
-   */
-  function getExpectedNumberOfActiveCards() {
-    let numSteps = PRIVACY_GUIDE_STEPS;
-    if (!shouldShowHistorySyncCard()) {
-      numSteps -= 1;
-    }
-    if (!shouldShowCookiesCard()) {
-      numSteps -= 1;
-    }
-    if (!shouldShowSafeBrowsingCard()) {
-      numSteps -= 1;
-    }
-    return numSteps;
-  }
-
-  function assertStepIndicatorModel(activeIndex: number) {
-    const model = page.computeStepIndicatorModel();
-    assertEquals(activeIndex, model.active);
-    assertEquals(getExpectedNumberOfActiveCards(), model.total);
-  }
-
-  function assertWelcomeCardVisible() {
-    assertQueryParameter(PrivacyGuideStep.WELCOME);
-    assertCardComponentsVisible({
-      isWelcomeFragmentVisibleExpected: true,
-    });
-  }
-
-  function assertCompletionCardVisible() {
-    assertQueryParameter(PrivacyGuideStep.COMPLETION);
-    assertCardComponentsVisible({
-      isCompletionFragmentVisibleExpected: true,
-    });
-  }
-
-  function assertMsbbCardVisible() {
-    assertQueryParameter(PrivacyGuideStep.MSBB);
-    assertCardComponentsVisible({
-      isSettingFooterVisibleExpected: true,
-      isBackButtonVisibleExpected: true,
-      isMsbbFragmentVisibleExpected: true,
-    });
-    assertStepIndicatorModel(0);
-  }
-
-  function assertHistorySyncCardVisible() {
-    assertQueryParameter(PrivacyGuideStep.HISTORY_SYNC);
-    assertCardComponentsVisible({
-      isSettingFooterVisibleExpected: true,
-      isBackButtonVisibleExpected: true,
-      isHistorySyncFragmentVisibleExpected: true,
-    });
-    assertStepIndicatorModel(1);
-  }
-
-  function assertSafeBrowsingCardVisible() {
-    assertQueryParameter(PrivacyGuideStep.SAFE_BROWSING);
-    assertCardComponentsVisible({
-      isSettingFooterVisibleExpected: true,
-      isBackButtonVisibleExpected: true,
-      isSafeBrowsingFragmentVisibleExpected: true,
-    });
-    assertStepIndicatorModel(shouldShowHistorySyncCard() ? 2 : 1);
-  }
-
-  function assertCookiesCardVisible() {
-    assertQueryParameter(PrivacyGuideStep.COOKIES);
-    assertCardComponentsVisible({
-      isSettingFooterVisibleExpected: true,
-      isBackButtonVisibleExpected: true,
-      isCookiesFragmentVisibleExpected: true,
-    });
-    let activeIndex = 3;
-    if (!shouldShowHistorySyncCard()) {
-      activeIndex -= 1;
-    }
-    if (!shouldShowSafeBrowsingCard()) {
-      activeIndex -= 1;
-    }
-    assertStepIndicatorModel(activeIndex);
-  }
-
   test('welcomeForwardNavigation', async function() {
     assertFalse(page.getPref('privacy_guide.viewed').value);
 
@@ -340,7 +375,7 @@
     // the welcome card.
     Router.getInstance().navigateTo(routes.PRIVACY_GUIDE);
     await flushTasks();
-    assertWelcomeCardVisible();
+    assertWelcomeCardVisible(page);
 
     assertTrue(page.getPref('privacy_guide.viewed').value);
 
@@ -349,7 +384,7 @@
             '#' + PrivacyGuideStep.WELCOME)!;
     welcomeFragment.$.startButton.click();
     flush();
-    assertMsbbCardVisible();
+    assertMsbbCardVisible(page, syncBrowserProxy);
 
     const result = await testMetricsBrowserProxy.whenCalled(
         'recordPrivacyGuideNextNavigationHistogram');
@@ -365,275 +400,19 @@
       syncAllDataTypes: true,
       typedUrlsSynced: true,
     });
-    assertMsbbCardVisible();
-  });
-
-  test('msbbBackNavigation', async function() {
-    await navigateToStep(PrivacyGuideStep.MSBB);
-    assertMsbbCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
-    flush();
-    assertWelcomeCardVisible();
-
-    const actionResult =
-        await testMetricsBrowserProxy.whenCalled('recordAction');
-    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickMSBB');
-  });
-
-  test('msbbForwardNavigationSyncOn', async function() {
-    await navigateToStep(PrivacyGuideStep.MSBB);
-    assertMsbbCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-    assertHistorySyncCardVisible();
-
-    const result = await testMetricsBrowserProxy.whenCalled(
-        'recordPrivacyGuideNextNavigationHistogram');
-    assertEquals(PrivacyGuideInteractions.MSBB_NEXT_BUTTON, result);
-
-    const actionResult =
-        await testMetricsBrowserProxy.whenCalled('recordAction');
-    assertEquals(actionResult, 'Settings.PrivacyGuide.NextClickMSBB');
-  });
-
-  test('msbbForwardNavigationSyncOff', async function() {
-    setupSync({
-      syncBrowserProxy: syncBrowserProxy,
-      syncOn: false,
-      syncAllDataTypes: false,
-      typedUrlsSynced: false,
-    });
-    await navigateToStep(PrivacyGuideStep.MSBB);
-    assertMsbbCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-    assertSafeBrowsingCardVisible();
-  });
-
-  test('historySyncBackNavigation', async function() {
-    await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
-    assertHistorySyncCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
-    assertMsbbCardVisible();
-
-    const actionResult =
-        await testMetricsBrowserProxy.whenCalled('recordAction');
-    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickHistorySync');
-  });
-
-  test('historySyncNavigatesAwayOnSyncOff', async function() {
-    await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
-    assertHistorySyncCardVisible();
-
-    // User disables sync while history sync card is shown.
-    setupSync({
-      syncBrowserProxy: syncBrowserProxy,
-      syncOn: false,
-      syncAllDataTypes: false,
-      typedUrlsSynced: false,
-    });
-    assertSafeBrowsingCardVisible();
-  });
-
-  test('historySyncNotReachableWhenSyncOff', async function() {
-    await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
-    setupSync({
-      syncBrowserProxy: syncBrowserProxy,
-      syncOn: false,
-      syncAllDataTypes: false,
-      typedUrlsSynced: false,
-    });
-    assertSafeBrowsingCardVisible();
-  });
-
-  test(
-      'historySyncCardForwardNavigationShouldShowSafeBrowsingCard',
-      async function() {
-        await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
-        assertHistorySyncCardVisible();
-
-        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-        assertSafeBrowsingCardVisible();
-
-        const result = await testMetricsBrowserProxy.whenCalled(
-            'recordPrivacyGuideNextNavigationHistogram');
-        assertEquals(PrivacyGuideInteractions.HISTORY_SYNC_NEXT_BUTTON, result);
-
-        const actionResult =
-            await testMetricsBrowserProxy.whenCalled('recordAction');
-        assertEquals(
-            actionResult, 'Settings.PrivacyGuide.NextClickHistorySync');
-      });
-
-  test(
-      'historySyncCardForwardNavigationShouldHideSafeBrowsingCard',
-      async function() {
-        setSafeBrowsingSetting(SafeBrowsingSetting.DISABLED);
-        await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
-        assertHistorySyncCardVisible();
-
-        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-        assertCookiesCardVisible();
-      });
-
-  test('safeBrowsingCardBackNavigationSyncOn', async function() {
-    await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
-    assertSafeBrowsingCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
-    assertHistorySyncCardVisible();
-
-    const actionResult =
-        await testMetricsBrowserProxy.whenCalled('recordAction');
-    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickSafeBrowsing');
-  });
-
-  test('safeBrowsingCardBackNavigationSyncOff', async function() {
-    setupSync({
-      syncBrowserProxy: syncBrowserProxy,
-      syncOn: false,
-      syncAllDataTypes: false,
-      typedUrlsSynced: false,
-    });
-    await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
-    assertSafeBrowsingCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
-    assertMsbbCardVisible();
-  });
-
-  test('safeBrowsingCardGetsUpdated', async function() {
-    await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
-    assertSafeBrowsingCardVisible();
-    const radioButtonGroup =
-        page.shadowRoot!.querySelector('#' + PrivacyGuideStep.SAFE_BROWSING)!
-            .shadowRoot!.querySelector<SettingsRadioGroupElement>(
-                '#safeBrowsingRadioGroup')!;
-    assertEquals(
-        Number(radioButtonGroup.selected), SafeBrowsingSetting.STANDARD);
-
-    // Changing the safe browsing setting should automatically change the
-    // selected radio button.
-    setSafeBrowsingSetting(SafeBrowsingSetting.ENHANCED);
-    assertEquals(
-        Number(radioButtonGroup.selected), SafeBrowsingSetting.ENHANCED);
-
-    // Changing the safe browsing setting to a disabled state while shown should
-    // navigate away from the safe browsing card.
-    setSafeBrowsingSetting(SafeBrowsingSetting.DISABLED);
-    assertCookiesCardVisible();
-  });
-
-  test(
-      'safeBrowsingCardForwardNavigationShouldShowCookiesCard',
-      async function() {
-        await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
-        assertSafeBrowsingCardVisible();
-
-        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-        flush();
-        assertCookiesCardVisible();
-
-        const result = await testMetricsBrowserProxy.whenCalled(
-            'recordPrivacyGuideNextNavigationHistogram');
-        assertEquals(
-            PrivacyGuideInteractions.SAFE_BROWSING_NEXT_BUTTON, result);
-
-        const actionResult =
-            await testMetricsBrowserProxy.whenCalled('recordAction');
-        assertEquals(
-            actionResult, 'Settings.PrivacyGuide.NextClickSafeBrowsing');
-      });
-
-  test(
-      'safeBrowsingCardForwardNavigationShouldHideCookiesCard',
-      async function() {
-        setCookieSetting(CookiePrimarySetting.ALLOW_ALL);
-        await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
-        assertSafeBrowsingCardVisible();
-
-        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-        flush();
-        assertCompletionCardVisible();
-      });
-
-  test('cookiesCardBackNavigationShouldShowSafeBrowsingCard', async function() {
-    await navigateToStep(PrivacyGuideStep.COOKIES);
-    assertCookiesCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
-    flush();
-    assertSafeBrowsingCardVisible();
-
-    const actionResult =
-        await testMetricsBrowserProxy.whenCalled('recordAction');
-    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickCookies');
-  });
-
-  test('cookiesCardBackNavigationShouldHideSafeBrowsingCard', async function() {
-    setSafeBrowsingSetting(SafeBrowsingSetting.DISABLED);
-    await navigateToStep(PrivacyGuideStep.COOKIES);
-    assertCookiesCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
-    flush();
-    assertHistorySyncCardVisible();
-  });
-
-  test('cookiesCardForwardNavigation', async function() {
-    await navigateToStep(PrivacyGuideStep.COOKIES);
-    assertCookiesCardVisible();
-
-    page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
-    flush();
-    assertCompletionCardVisible();
-
-    const result = await testMetricsBrowserProxy.whenCalled(
-        'recordPrivacyGuideNextNavigationHistogram');
-    assertEquals(PrivacyGuideInteractions.COOKIES_NEXT_BUTTON, result);
-
-    const actionResult =
-        await testMetricsBrowserProxy.whenCalled('recordAction');
-    assertEquals(actionResult, 'Settings.PrivacyGuide.NextClickCookies');
-  });
-
-  test('cookiesCardGetsUpdated', async function() {
-    await navigateToStep(PrivacyGuideStep.COOKIES);
-    assertCookiesCardVisible();
-    const radioButtonGroup =
-        page.shadowRoot!.querySelector('#' + PrivacyGuideStep.COOKIES)!
-            .shadowRoot!.querySelector<SettingsRadioGroupElement>(
-                '#cookiesRadioGroup')!;
-    assertEquals(
-        Number(radioButtonGroup.selected),
-        CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO);
-
-    // Changing the cookie setting should automatically change the selected
-    // radio button.
-    setCookieSetting(CookiePrimarySetting.BLOCK_THIRD_PARTY);
-    assertEquals(
-        Number(radioButtonGroup.selected),
-        CookiePrimarySetting.BLOCK_THIRD_PARTY);
-
-    // Changing the cookie setting to a non-third-party state while shown should
-    // navigate away from the cookies card.
-    setCookieSetting(CookiePrimarySetting.ALLOW_ALL);
-    await flushTasks();
-    assertCompletionCardVisible();
+    assertMsbbCardVisible(page, syncBrowserProxy);
   });
 
   test('completionCardBackNavigation', async function() {
     await navigateToStep(PrivacyGuideStep.COMPLETION);
-    assertCompletionCardVisible();
+    assertCompletionCardVisible(page);
 
     const completionFragment =
         page.shadowRoot!.querySelector('#' + PrivacyGuideStep.COMPLETION)!;
     completionFragment.shadowRoot!.querySelector<HTMLElement>(
                                       '#backButton')!.click();
     flush();
-    assertCookiesCardVisible();
+    assertCookiesCardVisible(page, syncBrowserProxy);
 
     const actionResult =
         await testMetricsBrowserProxy.whenCalled('recordAction');
@@ -649,7 +428,7 @@
     // Navigating to the privacy guide works.
     Router.getInstance().navigateTo(routes.PRIVACY_GUIDE);
     await flushTasks();
-    assertWelcomeCardVisible();
+    assertWelcomeCardVisible(page);
 
     // The user signs in to a child user account. This hides the privacy guide
     // and navigates away back to privacy settings page.
@@ -671,7 +450,7 @@
     // Navigating to the privacy guide works.
     Router.getInstance().navigateTo(routes.PRIVACY_GUIDE);
     await flushTasks();
-    assertWelcomeCardVisible();
+    assertWelcomeCardVisible(page);
 
     // The user signs in to a managed account. This hides the privacy guide and
     // navigates away back to privacy settings page.
@@ -706,11 +485,11 @@
     // Forward flow.
     await navigateToStep(PrivacyGuideStep.WELCOME);
     dispatchArrowRightEvent();
-    assertMsbbCardVisible();
+    assertMsbbCardVisible(page, syncBrowserProxy);
     dispatchArrowRightEvent();
-    assertHistorySyncCardVisible();
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
     dispatchArrowRightEvent();
-    assertSafeBrowsingCardVisible();
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
     // Arrow keys don't trigger a navigation when the focus is inside the radio
     // group.
     const sbRadioGroup =
@@ -718,12 +497,12 @@
             .querySelector<HTMLElement>('#' + PrivacyGuideStep.SAFE_BROWSING)!
             .shadowRoot!.querySelector<HTMLElement>('#safeBrowsingRadioGroup')!;
     sbRadioGroup.dispatchEvent(arrowLeftEvent);
-    assertSafeBrowsingCardVisible();
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
     sbRadioGroup.dispatchEvent(arrowRightEvent);
-    assertSafeBrowsingCardVisible();
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
 
     dispatchArrowRightEvent();
-    assertCookiesCardVisible();
+    assertCookiesCardVisible(page, syncBrowserProxy);
     // Arrow keys don't trigger a navigation when the focus is inside the radio
     // group.
     const cookiesRadioGroup =
@@ -731,30 +510,414 @@
             .querySelector<HTMLElement>('#' + PrivacyGuideStep.COOKIES)!
             .shadowRoot!.querySelector<HTMLElement>('#cookiesRadioGroup')!;
     cookiesRadioGroup.dispatchEvent(arrowLeftEvent);
-    assertCookiesCardVisible();
+    assertCookiesCardVisible(page, syncBrowserProxy);
     cookiesRadioGroup.dispatchEvent(arrowRightEvent);
-    assertCookiesCardVisible();
+    assertCookiesCardVisible(page, syncBrowserProxy);
 
     dispatchArrowRightEvent();
-    assertCompletionCardVisible();
+    assertCompletionCardVisible(page);
     // Forward navigation on the completion card does not trigger a navigation.
     dispatchArrowRightEvent();
-    assertCompletionCardVisible();
+    assertCompletionCardVisible(page);
 
     // Backward flow.
     dispatchArrowLeftEvent();
-    assertCookiesCardVisible();
+    assertCookiesCardVisible(page, syncBrowserProxy);
     dispatchArrowLeftEvent();
-    assertSafeBrowsingCardVisible();
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
     dispatchArrowLeftEvent();
-    assertHistorySyncCardVisible();
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
     dispatchArrowLeftEvent();
-    assertMsbbCardVisible();
+    assertMsbbCardVisible(page, syncBrowserProxy);
     dispatchArrowLeftEvent();
-    assertWelcomeCardVisible();
+    assertWelcomeCardVisible(page);
     // Backward navigation on the welcome card does not trigger a navigation.
     dispatchArrowLeftEvent();
-    assertWelcomeCardVisible();
+    assertWelcomeCardVisible(page);
+  });
+});
+
+suite('MsbbFragmentNavigations', function() {
+  let page: SettingsPrivacyGuidePageElement;
+  let settingsPrefs: SettingsPrefsElement;
+  let syncBrowserProxy: TestSyncBrowserProxy;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
+
+  suiteSetup(function() {
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
+    syncBrowserProxy = new TestSyncBrowserProxy();
+    syncBrowserProxy.testSyncStatus = null;
+    SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
+
+    page = createPrivacyGuidePageForTest(settingsPrefs);
+    setupPrivacyGuidePageForTest(page, syncBrowserProxy);
+
+    return flushTasks();
+  });
+
+  teardown(function() {
+    page.remove();
+    // The browser instance is shared among the tests, hence the route needs to
+    // be reset between tests.
+    Router.getInstance().navigateTo(routes.BASIC);
+  });
+
+  test('msbbBackNavigation', async function() {
+    await navigateToStep(PrivacyGuideStep.MSBB);
+    assertMsbbCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
+    flush();
+    assertWelcomeCardVisible(page);
+
+    const actionResult =
+        await testMetricsBrowserProxy.whenCalled('recordAction');
+    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickMSBB');
+  });
+
+  test('msbbForwardNavigationSyncOn', async function() {
+    await navigateToStep(PrivacyGuideStep.MSBB);
+    assertMsbbCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
+
+    const result = await testMetricsBrowserProxy.whenCalled(
+        'recordPrivacyGuideNextNavigationHistogram');
+    assertEquals(PrivacyGuideInteractions.MSBB_NEXT_BUTTON, result);
+
+    const actionResult =
+        await testMetricsBrowserProxy.whenCalled('recordAction');
+    assertEquals(actionResult, 'Settings.PrivacyGuide.NextClickMSBB');
+  });
+
+  test('msbbForwardNavigationSyncOff', async function() {
+    setupSync({
+      syncBrowserProxy: syncBrowserProxy,
+      syncOn: false,
+      syncAllDataTypes: false,
+      typedUrlsSynced: false,
+    });
+    await navigateToStep(PrivacyGuideStep.MSBB);
+    assertMsbbCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+  });
+});
+
+suite('HistorySyncFragmentNavigations', function() {
+  let page: SettingsPrivacyGuidePageElement;
+  let settingsPrefs: SettingsPrefsElement;
+  let syncBrowserProxy: TestSyncBrowserProxy;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
+
+  suiteSetup(function() {
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
+    syncBrowserProxy = new TestSyncBrowserProxy();
+    syncBrowserProxy.testSyncStatus = null;
+    SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
+
+    page = createPrivacyGuidePageForTest(settingsPrefs);
+    setupPrivacyGuidePageForTest(page, syncBrowserProxy);
+
+    return flushTasks();
+  });
+
+  teardown(function() {
+    page.remove();
+    // The browser instance is shared among the tests, hence the route needs to
+    // be reset between tests.
+    Router.getInstance().navigateTo(routes.BASIC);
+  });
+
+  test('historySyncBackNavigation', async function() {
+    await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
+    assertMsbbCardVisible(page, syncBrowserProxy);
+
+    const actionResult =
+        await testMetricsBrowserProxy.whenCalled('recordAction');
+    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickHistorySync');
+  });
+
+  test('historySyncNavigatesAwayOnSyncOff', async function() {
+    await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
+
+    // User disables sync while history sync card is shown.
+    setupSync({
+      syncBrowserProxy: syncBrowserProxy,
+      syncOn: false,
+      syncAllDataTypes: false,
+      typedUrlsSynced: false,
+    });
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+  });
+
+  test('historySyncNotReachableWhenSyncOff', async function() {
+    await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
+    setupSync({
+      syncBrowserProxy: syncBrowserProxy,
+      syncOn: false,
+      syncAllDataTypes: false,
+      typedUrlsSynced: false,
+    });
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+  });
+
+  test(
+      'historySyncCardForwardNavigationShouldShowSafeBrowsingCard',
+      async function() {
+        await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
+        assertHistorySyncCardVisible(page, syncBrowserProxy);
+
+        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+        assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+
+        const result = await testMetricsBrowserProxy.whenCalled(
+            'recordPrivacyGuideNextNavigationHistogram');
+        assertEquals(PrivacyGuideInteractions.HISTORY_SYNC_NEXT_BUTTON, result);
+
+        const actionResult =
+            await testMetricsBrowserProxy.whenCalled('recordAction');
+        assertEquals(
+            actionResult, 'Settings.PrivacyGuide.NextClickHistorySync');
+      });
+
+  test(
+      'historySyncCardForwardNavigationShouldHideSafeBrowsingCard',
+      async function() {
+        setSafeBrowsingSetting(page, SafeBrowsingSetting.DISABLED);
+        await navigateToStep(PrivacyGuideStep.HISTORY_SYNC);
+        assertHistorySyncCardVisible(page, syncBrowserProxy);
+
+        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+        assertCookiesCardVisible(page, syncBrowserProxy);
+      });
+});
+
+suite('SafeBrowsingFragmentNavigations', function() {
+  let page: SettingsPrivacyGuidePageElement;
+  let settingsPrefs: SettingsPrefsElement;
+  let syncBrowserProxy: TestSyncBrowserProxy;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
+
+  suiteSetup(function() {
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
+    syncBrowserProxy = new TestSyncBrowserProxy();
+    syncBrowserProxy.testSyncStatus = null;
+    SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
+
+    page = createPrivacyGuidePageForTest(settingsPrefs);
+    setupPrivacyGuidePageForTest(page, syncBrowserProxy);
+
+    return flushTasks();
+  });
+
+  teardown(function() {
+    page.remove();
+    // The browser instance is shared among the tests, hence the route needs to
+    // be reset between tests.
+    Router.getInstance().navigateTo(routes.BASIC);
+  });
+
+  test('safeBrowsingCardBackNavigationSyncOn', async function() {
+    await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
+
+    const actionResult =
+        await testMetricsBrowserProxy.whenCalled('recordAction');
+    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickSafeBrowsing');
+  });
+
+  test('safeBrowsingCardBackNavigationSyncOff', async function() {
+    setupSync({
+      syncBrowserProxy: syncBrowserProxy,
+      syncOn: false,
+      syncAllDataTypes: false,
+      typedUrlsSynced: false,
+    });
+    await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
+    assertMsbbCardVisible(page, syncBrowserProxy);
+  });
+
+  test('safeBrowsingCardGetsUpdated', async function() {
+    await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+    const radioButtonGroup =
+        page.shadowRoot!.querySelector('#' + PrivacyGuideStep.SAFE_BROWSING)!
+            .shadowRoot!.querySelector<SettingsRadioGroupElement>(
+                '#safeBrowsingRadioGroup')!;
+    assertEquals(
+        Number(radioButtonGroup.selected), SafeBrowsingSetting.STANDARD);
+
+    // Changing the safe browsing setting should automatically change the
+    // selected radio button.
+    setSafeBrowsingSetting(page, SafeBrowsingSetting.ENHANCED);
+    assertEquals(
+        Number(radioButtonGroup.selected), SafeBrowsingSetting.ENHANCED);
+
+    // Changing the safe browsing setting to a disabled state while shown should
+    // navigate away from the safe browsing card.
+    setSafeBrowsingSetting(page, SafeBrowsingSetting.DISABLED);
+    assertCookiesCardVisible(page, syncBrowserProxy);
+  });
+
+  test(
+      'safeBrowsingCardForwardNavigationShouldShowCookiesCard',
+      async function() {
+        await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
+        assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+
+        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+        flush();
+        assertCookiesCardVisible(page, syncBrowserProxy);
+
+        const result = await testMetricsBrowserProxy.whenCalled(
+            'recordPrivacyGuideNextNavigationHistogram');
+        assertEquals(
+            PrivacyGuideInteractions.SAFE_BROWSING_NEXT_BUTTON, result);
+
+        const actionResult =
+            await testMetricsBrowserProxy.whenCalled('recordAction');
+        assertEquals(
+            actionResult, 'Settings.PrivacyGuide.NextClickSafeBrowsing');
+      });
+
+  test(
+      'safeBrowsingCardForwardNavigationShouldHideCookiesCard',
+      async function() {
+        setCookieSetting(page, CookiePrimarySetting.ALLOW_ALL);
+        await navigateToStep(PrivacyGuideStep.SAFE_BROWSING);
+        assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+
+        page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+        flush();
+        assertCompletionCardVisible(page);
+      });
+});
+
+suite('CookiesFragmentNavigations', function() {
+  let page: SettingsPrivacyGuidePageElement;
+  let settingsPrefs: SettingsPrefsElement;
+  let syncBrowserProxy: TestSyncBrowserProxy;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
+
+  suiteSetup(function() {
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
+    syncBrowserProxy = new TestSyncBrowserProxy();
+    syncBrowserProxy.testSyncStatus = null;
+    SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
+
+    page = createPrivacyGuidePageForTest(settingsPrefs);
+    setupPrivacyGuidePageForTest(page, syncBrowserProxy);
+
+    return flushTasks();
+  });
+
+  teardown(function() {
+    page.remove();
+    // The browser instance is shared among the tests, hence the route needs to
+    // be reset between tests.
+    Router.getInstance().navigateTo(routes.BASIC);
+  });
+
+  test('cookiesCardBackNavigationShouldShowSafeBrowsingCard', async function() {
+    await navigateToStep(PrivacyGuideStep.COOKIES);
+    assertCookiesCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
+    flush();
+    assertSafeBrowsingCardVisible(page, syncBrowserProxy);
+
+    const actionResult =
+        await testMetricsBrowserProxy.whenCalled('recordAction');
+    assertEquals(actionResult, 'Settings.PrivacyGuide.BackClickCookies');
+  });
+
+  test('cookiesCardBackNavigationShouldHideSafeBrowsingCard', async function() {
+    setSafeBrowsingSetting(page, SafeBrowsingSetting.DISABLED);
+    await navigateToStep(PrivacyGuideStep.COOKIES);
+    assertCookiesCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
+    flush();
+    assertHistorySyncCardVisible(page, syncBrowserProxy);
+  });
+
+  test('cookiesCardForwardNavigation', async function() {
+    await navigateToStep(PrivacyGuideStep.COOKIES);
+    assertCookiesCardVisible(page, syncBrowserProxy);
+
+    page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
+    flush();
+    assertCompletionCardVisible(page);
+
+    const result = await testMetricsBrowserProxy.whenCalled(
+        'recordPrivacyGuideNextNavigationHistogram');
+    assertEquals(PrivacyGuideInteractions.COOKIES_NEXT_BUTTON, result);
+
+    const actionResult =
+        await testMetricsBrowserProxy.whenCalled('recordAction');
+    assertEquals(actionResult, 'Settings.PrivacyGuide.NextClickCookies');
+  });
+
+  test('cookiesCardGetsUpdated', async function() {
+    await navigateToStep(PrivacyGuideStep.COOKIES);
+    assertCookiesCardVisible(page, syncBrowserProxy);
+    const radioButtonGroup =
+        page.shadowRoot!.querySelector('#' + PrivacyGuideStep.COOKIES)!
+            .shadowRoot!.querySelector<SettingsRadioGroupElement>(
+                '#cookiesRadioGroup')!;
+    assertEquals(
+        Number(radioButtonGroup.selected),
+        CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO);
+
+    // Changing the cookie setting should automatically change the selected
+    // radio button.
+    setCookieSetting(page, CookiePrimarySetting.BLOCK_THIRD_PARTY);
+    assertEquals(
+        Number(radioButtonGroup.selected),
+        CookiePrimarySetting.BLOCK_THIRD_PARTY);
+
+    // Changing the cookie setting to a non-third-party state while shown should
+    // navigate away from the cookies card.
+    setCookieSetting(page, CookiePrimarySetting.ALLOW_ALL);
+    await flushTasks();
+    assertCompletionCardVisible(page);
   });
 });
 
@@ -776,13 +939,7 @@
     syncBrowserProxy.testSyncStatus = null;
     SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
 
-    document.body.innerHTML = '';
-    page = document.createElement('settings-privacy-guide-page');
-    page.disableAnimationsForTesting();
-    page.prefs = settingsPrefs.prefs!;
-    document.body.appendChild(page);
-
-    setupPrivacyRouteForTest();
+    page = createPrivacyGuidePageForTest(settingsPrefs);
 
     return flushTasks();
   });
diff --git a/chrome/updater/app/app_install.cc b/chrome/updater/app/app_install.cc
index 08e45272..8c6fee9f 100644
--- a/chrome/updater/app/app_install.cc
+++ b/chrome/updater/app/app_install.cc
@@ -23,6 +23,7 @@
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
+#include "chrome/updater/external_constants.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/registration_data.h"
@@ -93,7 +94,8 @@
 AppInstall::AppInstall(SplashScreen::Maker splash_screen_maker,
                        AppInstallController::Maker app_install_controller_maker)
     : splash_screen_maker_(std::move(splash_screen_maker)),
-      app_install_controller_maker_(app_install_controller_maker) {
+      app_install_controller_maker_(app_install_controller_maker),
+      external_constants_(CreateExternalConstants()) {
   DCHECK(splash_screen_maker_);
   DCHECK(app_install_controller_maker_);
 }
@@ -138,7 +140,8 @@
 
   // Creating instances of `UpdateServiceProxy` is possible only after task
   // scheduling has been initialized.
-  update_service_ = CreateUpdateServiceProxy(updater_scope());
+  update_service_ = CreateUpdateServiceProxy(
+      updater_scope(), external_constants_->OverinstallTimeout());
   update_service_->GetVersion(
       base::BindOnce(&AppInstall::GetVersionDone, this));
 }
@@ -177,7 +180,17 @@
     return;
   }
 
-  WakeCandidate();
+  // It's possible that a previous updater existed but is nonresponsive. In
+  // this case, clear the active version in global prefs so that the system can
+  // recover.
+  base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})
+      ->PostTaskAndReply(FROM_HERE,
+                         base::BindOnce(
+                             [](UpdaterScope scope) {
+                               CreateGlobalPrefs(scope)->SetActiveVersion("");
+                             },
+                             updater_scope()),
+                         base::BindOnce(&AppInstall::WakeCandidate, this));
 }
 
 void AppInstall::WakeCandidate() {
@@ -210,7 +223,8 @@
 #if BUILDFLAG(IS_MAC)
   // TODO(crbug.com/1297163) - encapsulate the reinitialization of the
   // proxy server instance to avoid this special case.
-  update_service_ = CreateUpdateServiceProxy(updater_scope());
+  update_service_ = CreateUpdateServiceProxy(
+      updater_scope(), external_constants_->OverinstallTimeout());
 #endif
 
   RegistrationRequest request;
diff --git a/chrome/updater/app/app_install.h b/chrome/updater/app/app_install.h
index f087c7b..1b347ad 100644
--- a/chrome/updater/app/app_install.h
+++ b/chrome/updater/app/app_install.h
@@ -21,6 +21,7 @@
 
 namespace updater {
 
+class ExternalConstants;
 class UpdateService;
 
 // This class defines an interface for installing an application. The interface
@@ -98,6 +99,8 @@
 
   scoped_refptr<AppInstallController> app_install_controller_;
 
+  scoped_refptr<ExternalConstants> external_constants_;
+
   scoped_refptr<UpdateService> update_service_;
 };
 
diff --git a/chrome/updater/app/app_server.cc b/chrome/updater/app/app_server.cc
index 0a95493..60e4fad 100644
--- a/chrome/updater/app/app_server.cc
+++ b/chrome/updater/app/app_server.cc
@@ -58,7 +58,10 @@
   }
 
   const base::Version this_version(kUpdaterVersion);
-  const base::Version active_version(global_prefs->GetActiveVersion());
+  base::Version active_version(global_prefs->GetActiveVersion());
+  if (!active_version.IsValid()) {
+    active_version = base::Version(std::vector<uint32_t>{0});
+  }
 
   VLOG(2) << "This version: " << this_version.GetString()
           << ", active version: " << active_version.GetString();
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc
index b0694186..884a7c7a 100644
--- a/chrome/updater/constants.cc
+++ b/chrome/updater/constants.cc
@@ -80,6 +80,7 @@
 const char kDevOverrideKeyServerKeepAliveSeconds[] = "server_keep_alive";
 const char kDevOverrideKeyCrxVerifierFormat[] = "crx_verifier_format";
 const char kDevOverrideKeyGroupPolicies[] = "group_policies";
+const char kDevOverrideKeyOverinstallTimeout[] = "overinstall_timeout";
 
 // Developer override file name, relative to app data directory.
 const char kDevOverrideFileName[] = "overrides.json";
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index f7da595..2654b1e8 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -186,6 +186,7 @@
 extern const char kDevOverrideKeyServerKeepAliveSeconds[];
 extern const char kDevOverrideKeyCrxVerifierFormat[];
 extern const char kDevOverrideKeyGroupPolicies[];
+extern const char kDevOverrideKeyOverinstallTimeout[];
 
 // File name of developer overrides file.
 extern const char kDevOverrideFileName[];
diff --git a/chrome/updater/external_constants.h b/chrome/updater/external_constants.h
index 8bd8e56..80d546c 100644
--- a/chrome/updater/external_constants.h
+++ b/chrome/updater/external_constants.h
@@ -13,6 +13,10 @@
 
 class GURL;
 
+namespace base {
+class TimeDelta;
+}
+
 namespace crx_file {
 enum class VerifierFormat;
 }
@@ -46,6 +50,9 @@
   // Overrides for the `GroupPolicyManager`.
   virtual base::Value::Dict GroupPolicies() const = 0;
 
+  // Overrides the overinstall timeout.
+  virtual base::TimeDelta OverinstallTimeout() const = 0;
+
  protected:
   friend class base::RefCountedThreadSafe<ExternalConstants>;
   scoped_refptr<ExternalConstants> next_provider_;
diff --git a/chrome/updater/external_constants_builder.cc b/chrome/updater/external_constants_builder.cc
index c2ed04c1..3ed8cd9 100644
--- a/chrome/updater/external_constants_builder.cc
+++ b/chrome/updater/external_constants_builder.cc
@@ -11,6 +11,7 @@
 
 #include "base/json/json_file_value_serializer.h"
 #include "base/logging.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/external_constants_default.h"
@@ -117,6 +118,13 @@
   return *this;
 }
 
+ExternalConstantsBuilder& ExternalConstantsBuilder::SetOverinstallTimeout(
+    const base::TimeDelta& overinstall_timeout) {
+  overrides_.Set(kDevOverrideKeyOverinstallTimeout,
+                 static_cast<int>(overinstall_timeout.InSeconds()));
+  return *this;
+}
+
 bool ExternalConstantsBuilder::Overwrite() {
   const absl::optional<base::FilePath> base_path =
       GetBaseDataDirectory(GetUpdaterScope());
@@ -151,6 +159,8 @@
     SetCrxVerifierFormat(verifier->CrxVerifierFormat());
   if (!overrides_.contains(kDevOverrideKeyGroupPolicies))
     SetGroupPolicies(verifier->GroupPolicies());
+  if (!overrides_.contains(kDevOverrideKeyOverinstallTimeout))
+    SetOverinstallTimeout(verifier->OverinstallTimeout());
 
   return Overwrite();
 }
diff --git a/chrome/updater/external_constants_builder.h b/chrome/updater/external_constants_builder.h
index 5af4a6cc..aaf5e24c 100644
--- a/chrome/updater/external_constants_builder.h
+++ b/chrome/updater/external_constants_builder.h
@@ -11,6 +11,10 @@
 #include "base/files/file_path.h"
 #include "base/values.h"
 
+namespace base {
+class TimeDelta;
+}
+
 namespace crx_file {
 enum class VerifierFormat;
 }
@@ -55,6 +59,9 @@
       const base::Value::Dict& group_policies);
   ExternalConstantsBuilder& ClearGroupPolicies();
 
+  ExternalConstantsBuilder& SetOverinstallTimeout(
+      const base::TimeDelta& overinstall_timeout);
+
   // Write the external constants overrides file in the default location
   // with the values that have been previously set, replacing any file
   // previously there. The builder remains usable, does not forget its state,
diff --git a/chrome/updater/external_constants_default.cc b/chrome/updater/external_constants_default.cc
index ddb4911a..a035ad5 100644
--- a/chrome/updater/external_constants_default.cc
+++ b/chrome/updater/external_constants_default.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/external_constants_default.h"
 
 #include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/external_constants.h"
@@ -40,6 +41,10 @@
     return base::Value::Dict();
   }
 
+  base::TimeDelta OverinstallTimeout() const override {
+    return base::Minutes(2);
+  }
+
  private:
   ~DefaultExternalConstants() override = default;
 };
diff --git a/chrome/updater/external_constants_override.cc b/chrome/updater/external_constants_override.cc
index 1f71937..e009039 100644
--- a/chrome/updater/external_constants_override.cc
+++ b/chrome/updater/external_constants_override.cc
@@ -15,6 +15,7 @@
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
@@ -147,6 +148,19 @@
   return group_policies_value->GetDict().Clone();
 }
 
+base::TimeDelta ExternalConstantsOverrider::OverinstallTimeout() const {
+  if (!override_values_.contains(kDevOverrideKeyOverinstallTimeout)) {
+    return next_provider_->OverinstallTimeout();
+  }
+
+  const base::Value* value =
+      override_values_.Find(kDevOverrideKeyOverinstallTimeout);
+  CHECK(value->is_int()) << "Unexpected type of override["
+                         << kDevOverrideKeyOverinstallTimeout
+                         << "]: " << base::Value::GetTypeName(value->type());
+  return base::Seconds(value->GetInt());
+}
+
 // static
 scoped_refptr<ExternalConstantsOverrider>
 ExternalConstantsOverrider::FromDefaultJSONFile(
diff --git a/chrome/updater/external_constants_override.h b/chrome/updater/external_constants_override.h
index 5338a66..d58b9c6 100644
--- a/chrome/updater/external_constants_override.h
+++ b/chrome/updater/external_constants_override.h
@@ -17,6 +17,7 @@
 class GURL;
 
 namespace base {
+class TimeDelta;
 class Value;
 }
 
@@ -46,6 +47,7 @@
   int ServerKeepAliveSeconds() const override;
   crx_file::VerifierFormat CrxVerifierFormat() const override;
   base::Value::Dict GroupPolicies() const override;
+  base::TimeDelta OverinstallTimeout() const override;
 
  private:
   const base::Value::Dict override_values_;
diff --git a/chrome/updater/linux/update_service_proxy.cc b/chrome/updater/linux/update_service_proxy.cc
index f0e889b..3af7e226 100644
--- a/chrome/updater/linux/update_service_proxy.cc
+++ b/chrome/updater/linux/update_service_proxy.cc
@@ -6,6 +6,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
+#include "base/time/time.h"
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_scope.h"
 
@@ -13,7 +14,8 @@
 
 // TODO(crbug.com/1276169) - implement.
 scoped_refptr<UpdateService> CreateUpdateServiceProxy(
-    UpdaterScope /*updater_scope*/) {
+    UpdaterScope /*updater_scope*/,
+    const base::TimeDelta& get_version_timeout) {
   NOTIMPLEMENTED();
   return nullptr;
 }
diff --git a/chrome/updater/mac/keystone/ksadmin.mm b/chrome/updater/mac/keystone/ksadmin.mm
index cbae592..daf6447f 100644
--- a/chrome/updater/mac/keystone/ksadmin.mm
+++ b/chrome/updater/mac/keystone/ksadmin.mm
@@ -32,6 +32,7 @@
 #include "base/task/thread_pool.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
 #include "chrome/updater/app/app.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/mac/mac_util.h"
@@ -225,10 +226,8 @@
  public:
   explicit KSAdminApp(const std::map<std::string, std::string>& switches)
       : switches_(switches),
-        system_service_proxy_(
-            base::MakeRefCounted<UpdateServiceProxy>(UpdaterScope::kSystem)),
-        user_service_proxy_(
-            base::MakeRefCounted<UpdateServiceProxy>(UpdaterScope::kUser)) {}
+        system_service_proxy_(CreateUpdateServiceProxy(UpdaterScope::kSystem)),
+        user_service_proxy_(CreateUpdateServiceProxy(UpdaterScope::kUser)) {}
 
  private:
   ~KSAdminApp() override = default;
@@ -250,7 +249,7 @@
   int PrintKeystoneTag(const std::string& app_id) const;
   void PrintKeystoneTickets(const std::string& app_id) const;
 
-  scoped_refptr<UpdateServiceProxy> ServiceProxy(UpdaterScope scope) const;
+  scoped_refptr<UpdateService> ServiceProxy(UpdaterScope scope) const;
   void ChooseService(
       base::OnceCallback<void(UpdaterScope scope)> callback) const;
 
@@ -260,11 +259,11 @@
   NSDictionary<NSString*, KSTicket*>* LoadTicketStore() const;
 
   const std::map<std::string, std::string> switches_;
-  scoped_refptr<UpdateServiceProxy> system_service_proxy_;
-  scoped_refptr<UpdateServiceProxy> user_service_proxy_;
+  scoped_refptr<UpdateService> system_service_proxy_;
+  scoped_refptr<UpdateService> user_service_proxy_;
 };
 
-scoped_refptr<UpdateServiceProxy> KSAdminApp::ServiceProxy(
+scoped_refptr<UpdateService> KSAdminApp::ServiceProxy(
     UpdaterScope scope) const {
   return scope == UpdaterScope::kSystem ? system_service_proxy_
                                         : user_service_proxy_;
diff --git a/chrome/updater/mac/update_service_proxy.h b/chrome/updater/mac/update_service_proxy.h
index 742eb56..fac828a 100644
--- a/chrome/updater/mac/update_service_proxy.h
+++ b/chrome/updater/mac/update_service_proxy.h
@@ -15,6 +15,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_scope.h"
 
@@ -34,7 +35,8 @@
 // All functions and callbacks must be called on the same sequence.
 class UpdateServiceProxy : public UpdateService {
  public:
-  explicit UpdateServiceProxy(UpdaterScope scope);
+  UpdateServiceProxy(UpdaterScope scope,
+                     const base::TimeDelta& get_version_timeout);
 
   // Overrides for UpdateService.
   void GetVersion(
@@ -72,6 +74,7 @@
   SEQUENCE_CHECKER(sequence_checker_);
 
   UpdaterScope scope_;
+  base::TimeDelta get_version_timeout_;
   base::scoped_nsobject<CRUUpdateServiceProxyImpl> client_;
   scoped_refptr<base::SequencedTaskRunner> callback_runner_;
 };
diff --git a/chrome/updater/mac/update_service_proxy.mm b/chrome/updater/mac/update_service_proxy.mm
index 0b86e04..95cfccc 100644
--- a/chrome/updater/mac/update_service_proxy.mm
+++ b/chrome/updater/mac/update_service_proxy.mm
@@ -18,6 +18,7 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
 #include "base/version.h"
 #import "chrome/updater/app/server/mac/service_protocol.h"
 #import "chrome/updater/app/server/mac/update_service_wrappers.h"
@@ -201,11 +202,16 @@
 namespace updater {
 
 scoped_refptr<UpdateService> CreateUpdateServiceProxy(
-    UpdaterScope updater_scope) {
-  return base::MakeRefCounted<UpdateServiceProxy>(updater_scope);
+    UpdaterScope updater_scope,
+    const base::TimeDelta& get_version_timeout) {
+  return base::MakeRefCounted<UpdateServiceProxy>(updater_scope,
+                                                  get_version_timeout);
 }
 
-UpdateServiceProxy::UpdateServiceProxy(UpdaterScope scope) : scope_(scope) {
+UpdateServiceProxy::UpdateServiceProxy(
+    UpdaterScope scope,
+    const base::TimeDelta& get_version_timeout)
+    : scope_(scope), get_version_timeout_(get_version_timeout) {
   client_.reset([[CRUUpdateServiceProxyImpl alloc] initWithScope:scope]);
   callback_runner_ = base::SequencedTaskRunnerHandle::Get();
 }
@@ -214,10 +220,11 @@
     base::OnceCallback<void(const base::Version&)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  VLOG(2) << __func__ << " with timeout " << get_version_timeout_;
   auto timeout_callback = std::make_unique<base::CancelableOnceClosure>(
       base::BindOnce(&UpdateServiceProxy::Reset, base::Unretained(this)));
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, timeout_callback->callback(), base::Minutes(2));
+      FROM_HERE, timeout_callback->callback(), get_version_timeout_);
 
   __block base::OnceCallback<void(const base::Version&)> block_callback =
       std::move(callback).Then(base::BindOnce(
diff --git a/chrome/updater/mac/update_service_proxy_test.mm b/chrome/updater/mac/update_service_proxy_test.mm
index 235c5d2..3982456 100644
--- a/chrome/updater/mac/update_service_proxy_test.mm
+++ b/chrome/updater/mac/update_service_proxy_test.mm
@@ -293,8 +293,8 @@
   run_loop_ = std::make_unique<base::RunLoop>();
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([this]() {
-        service_ =
-            base::MakeRefCounted<UpdateServiceProxy>(UpdaterScope::kUser);
+        service_ = base::MakeRefCounted<UpdateServiceProxy>(
+            UpdaterScope::kUser, base::TimeDelta::Max());
       }));
 }
 
diff --git a/chrome/updater/service_proxy_factory.h b/chrome/updater/service_proxy_factory.h
index 72dba914..f7a3450 100644
--- a/chrome/updater/service_proxy_factory.h
+++ b/chrome/updater/service_proxy_factory.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_SERVICE_PROXY_FACTORY_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
 
 namespace updater {
 
@@ -14,7 +15,8 @@
 class UpdateServiceInternal;
 
 scoped_refptr<UpdateService> CreateUpdateServiceProxy(
-    UpdaterScope updater_scope);
+    UpdaterScope updater_scope,
+    const base::TimeDelta& get_version_timeout = base::TimeDelta::Max());
 
 scoped_refptr<UpdateServiceInternal> CreateUpdateServiceInternalProxy(
     UpdaterScope updater_scope);
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index 4ecbeb1..c3c8420d 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -65,6 +65,7 @@
   virtual void Update(const std::string& app_id,
                       const std::string& install_data_index) const = 0;
   virtual void UpdateAll() const = 0;
+  virtual void DeleteUpdaterDirectory() const = 0;
   virtual void PrintLog() const = 0;
   virtual base::FilePath GetDifferentUserPath() const = 0;
   virtual void WaitForUpdaterExit() const = 0;
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index c995618..fbcb3031 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -178,6 +178,10 @@
 
   void UpdateAll() const override { RunCommand("update_all", {}); }
 
+  void DeleteUpdaterDirectory() const override {
+    RunCommand("delete_updater_directory", {});
+  }
+
   void InstallApp(const std::string& app_id) const override {
     RunCommand("install_app", {Param("app_id", app_id)});
   }
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index 906d2e7..9eb9ef6 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -152,6 +152,10 @@
 
   void UpdateAll() const override { updater::test::UpdateAll(updater_scope_); }
 
+  void DeleteUpdaterDirectory() const override {
+    updater::test::DeleteUpdaterDirectory(updater_scope_);
+  }
+
   void InstallApp(const std::string& app_id) const override {
     updater::test::InstallApp(updater_scope_, app_id);
   }
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index 9bbf9dc..984db457 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -241,6 +241,8 @@
 
   void UpdateAll() { test_commands_->UpdateAll(); }
 
+  void DeleteUpdaterDirectory() { test_commands_->DeleteUpdaterDirectory(); }
+
   base::FilePath GetDifferentUserPath() {
     return test_commands_->GetDifferentUserPath();
   }
@@ -338,6 +340,34 @@
   Uninstall();
 }
 
+TEST_F(IntegrationTest, OverinstallWorking) {
+  SetupRealUpdaterLowerVersion();
+  WaitForUpdaterExit();
+  ExpectVersionNotActive(kUpdaterVersion);
+
+  // A new version hands off installation to the old version, and doesn't
+  // change the active version of the updater.
+  Install();
+  WaitForUpdaterExit();
+  ExpectVersionNotActive(kUpdaterVersion);
+
+  Uninstall();
+}
+
+TEST_F(IntegrationTest, OverinstallBroken) {
+  SetupRealUpdaterLowerVersion();
+  WaitForUpdaterExit();
+  DeleteUpdaterDirectory();
+
+  // Since the old version is not working, the new version should install and
+  // become active.
+  Install();
+  WaitForUpdaterExit();
+  ExpectVersionActive(kUpdaterVersion);
+
+  Uninstall();
+}
+
 TEST_F(IntegrationTest, SelfUninstallOutdatedUpdater) {
   Install();
   ExpectInstalled();
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index 4d14a70..c30b9a1d 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -282,6 +282,8 @@
      WithSwitch("install_data_index",
                 (WithSwitch("app_id", WithSystemScope(Wrap(&Update)))))},
     {"update_all", WithSystemScope(Wrap(&UpdateAll))},
+    {"delete_updater_directory",
+     WithSystemScope(Wrap(&DeleteUpdaterDirectory))},
     {"install_app", WithSwitch("app_id", WithSystemScope(Wrap(&InstallApp)))},
     {"uninstall_app",
      WithSwitch("app_id", WithSystemScope(Wrap(&UninstallApp)))},
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 959a1d9..906a6e7 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -322,6 +322,12 @@
   loop.Run();
 }
 
+void DeleteUpdaterDirectory(UpdaterScope scope) {
+  absl::optional<base::FilePath> install_dir = GetBaseInstallDirectory(scope);
+  ASSERT_TRUE(install_dir);
+  ASSERT_TRUE(base::DeletePathRecursively(*install_dir));
+}
+
 void SetupFakeUpdaterPrefs(UpdaterScope scope, const base::Version& version) {
   scoped_refptr<GlobalPrefs> global_prefs = CreateGlobalPrefs(scope);
   ASSERT_TRUE(global_prefs) << "No global prefs.";
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index a0f1b18..cf4aa22d 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -99,6 +99,11 @@
 // Invokes the active instance's UpdateService::UpdateAll (via RPC).
 void UpdateAll(UpdaterScope scope);
 
+// Deletes the updater executable directory. Does not do any kind of cleanup
+// related to service registration. The intent of this command is to replicate
+// a common mode of breaking the updater, so we can test how it recovers.
+void DeleteUpdaterDirectory(UpdaterScope scope);
+
 // Runs the command and waits for it to exit or time out.
 bool Run(UpdaterScope scope, base::CommandLine command_line, int* exit_code);
 
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm
index 39399253..5393f4d 100644
--- a/chrome/updater/test/integration_tests_mac.mm
+++ b/chrome/updater/test/integration_tests_mac.mm
@@ -120,6 +120,7 @@
                   .SetInitialDelay(0.1)
                   .SetServerKeepAliveSeconds(1)
                   .SetCrxVerifierFormat(crx_file::VerifierFormat::CRX3)
+                  .SetOverinstallTimeout(base::Seconds(11))
                   .Modify());
 }
 
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index 3254241..d2bf5ed 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -507,6 +507,7 @@
                   .SetUseCUP(false)
                   .SetInitialDelay(0.1)
                   .SetCrxVerifierFormat(crx_file::VerifierFormat::CRX3)
+                  .SetOverinstallTimeout(base::Seconds(11))
                   .Modify());
 }
 
diff --git a/chrome/updater/win/update_service_proxy.cc b/chrome/updater/win/update_service_proxy.cc
index 6da6eaa6..b36ec5b 100644
--- a/chrome/updater/win/update_service_proxy.cc
+++ b/chrome/updater/win/update_service_proxy.cc
@@ -340,7 +340,8 @@
 }  // namespace
 
 scoped_refptr<UpdateService> CreateUpdateServiceProxy(
-    UpdaterScope updater_scope) {
+    UpdaterScope updater_scope,
+    const base::TimeDelta& /*get_version_timeout*/) {
   return base::MakeRefCounted<UpdateServiceProxy>(updater_scope);
 }
 
diff --git a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc
index 5411505..db8809a 100644
--- a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc
+++ b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.cc
@@ -56,6 +56,11 @@
 struct FakeUserDataAuthClient::UserCryptohomeState {
   // Maps labels to auth factors.
   std::map<std::string, FakeAuthFactor> auth_factors;
+
+  // A flag describing how we pretend that the user's home directory is
+  // encrypted.
+  HomeEncryptionMethod home_encryption_method =
+      HomeEncryptionMethod::kDirCrypto;
 };
 
 namespace {
@@ -141,10 +146,20 @@
   client_->RunPendingWaitForServiceToBeAvailableCallbacks();
 }
 
-void FakeUserDataAuthClient::TestApi::SetEcryptfsUserHome(
+void FakeUserDataAuthClient::TestApi::SetHomeEncryptionMethod(
     const cryptohome::AccountIdentifier& cryptohome_id,
-    bool use_ecryptfs) {
-  client_->SetEcryptfsUserHome(cryptohome_id, use_ecryptfs);
+    HomeEncryptionMethod method) {
+  auto user_it = client_->users_.find(cryptohome_id);
+  if (user_it == std::end(client_->users_)) {
+    LOG(ERROR) << "User does not exist: " << cryptohome_id.account_id();
+    // TODO(crbug.com/1334538): Some existing tests rely on us creating the
+    // user here, but new tests shouldn't. Eventually this should crash.
+    user_it =
+        client_->users_.insert({cryptohome_id, UserCryptohomeState()}).first;
+  }
+  DCHECK(user_it != std::end(client_->users_));
+  UserCryptohomeState& user_state = user_it->second;
+  user_state.home_encryption_method = method;
 }
 
 void FakeUserDataAuthClient::TestApi::SetPinLocked(
@@ -207,38 +222,69 @@
 void FakeUserDataAuthClient::Mount(
     const ::user_data_auth::MountRequest& request,
     MountCallback callback) {
-  ::user_data_auth::CryptohomeErrorCode error = cryptohome_error_;
   last_mount_request_ = request;
   ++mount_request_count_;
+
   ::user_data_auth::MountReply reply;
+  ReplyOnReturn auto_reply(&reply, std::move(callback));
 
-  cryptohome::AccountIdentifier account;
-  if (request.guest_mount()) {
-    account.set_account_id(kGuestUserName);
-    reply.set_sanitized_username(GetStubSanitizedUsername(account));
-  } else {
-    if (request.has_account()) {
-      account = request.account();
-      reply.set_sanitized_username(GetStubSanitizedUsername(account));
-      if (TestApi::Get()->mount_create_required_ && !request.has_create())
-        error = ::user_data_auth::CryptohomeErrorCode::
-            CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND;
-    } else {
-      auto auth_session = auth_sessions_.find(request.auth_session_id());
-      DCHECK(auth_session != std::end(auth_sessions_));
-      account = auth_session->second.account;
-    }
-
-    reply.set_sanitized_username(GetStubSanitizedUsername(account));
-    if (IsEcryptfsUserHome(account) && !request.to_migrate_from_ecryptfs() &&
-        request.force_dircrypto_if_available()) {
-      error = ::user_data_auth::CryptohomeErrorCode::
-          CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION;
-    }
+  if (cryptohome_error_ !=
+      ::user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) {
+    reply.set_error(cryptohome_error_);
+    return;
   }
 
-  reply.set_error(error);
-  ReturnProtobufMethodCallback(reply, std::move(callback));
+  if (request.guest_mount()) {
+    cryptohome::AccountIdentifier account_id;
+    account_id.set_account_id(kGuestUserName);
+    reply.set_sanitized_username(GetStubSanitizedUsername(account_id));
+    return;
+  }
+
+  // TODO(crbug.com/1334538): We should get rid of mount_create_required_
+  // and instead check whether the user exists or not here. Tests would then
+  // need to set up a user (or not).
+  if (TestApi::Get()->mount_create_required_ && !request.has_create()) {
+    reply.set_error(::user_data_auth::CryptohomeErrorCode::
+                        CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND);
+    return;
+  }
+
+  const cryptohome::AccountIdentifier* account_id;
+  if (request.has_account()) {
+    account_id = &request.account();
+  } else {
+    auto auth_session = auth_sessions_.find(request.auth_session_id());
+    CHECK(auth_session != std::end(auth_sessions_))
+        << "Invalid account session";
+    account_id = &auth_session->second.account;
+  }
+  DCHECK(account_id);
+
+  auto user_it = users_.find(*account_id);
+  if (user_it == std::end(users_)) {
+    LOG_IF(ERROR, !request.has_create())
+        << "UserDataAuth::Mount called without create field for nonexistant "
+           "user: "
+        << account_id->account_id();
+    // TODO(crbug.com/1334538): Old tests rely on this behavior, but new tests
+    // shouldn't: Instead, they should either fill in the `create` field, or
+    // they should set up a test user first.
+    user_it = users_.insert({*account_id, UserCryptohomeState()}).first;
+  }
+  DCHECK(user_it != std::end(users_));
+  const UserCryptohomeState& user_state = user_it->second;
+
+  const bool is_ecryptfs =
+      user_state.home_encryption_method == HomeEncryptionMethod::kEcryptfs;
+  if (is_ecryptfs && !request.to_migrate_from_ecryptfs() &&
+      request.force_dircrypto_if_available()) {
+    reply.set_error(::user_data_auth::CryptohomeErrorCode::
+                        CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION);
+    return;
+  }
+
+  reply.set_sanitized_username(GetStubSanitizedUsername(*account_id));
 }
 void FakeUserDataAuthClient::Remove(
     const ::user_data_auth::RemoveRequest& request,
@@ -500,8 +546,24 @@
     const ::user_data_auth::NeedsDircryptoMigrationRequest& request,
     NeedsDircryptoMigrationCallback callback) {
   ::user_data_auth::NeedsDircryptoMigrationReply reply;
-  reply.set_needs_dircrypto_migration(IsEcryptfsUserHome(request.account_id()));
-  ReturnProtobufMethodCallback(reply, std::move(callback));
+  ReplyOnReturn auto_reply(&reply, std::move(callback));
+
+  const cryptohome::AccountIdentifier& account_id = request.account_id();
+
+  const auto user_it = users_.find(account_id);
+  if (user_it == std::end(users_)) {
+    // TODO(crbug.com/1334538): New tests shouldn't rely on this behavior and
+    // instead set up the user first.
+    LOG(ERROR) << "User does not exist: " << account_id.account_id();
+    reply.set_needs_dircrypto_migration(false);
+    return;
+  }
+  DCHECK(user_it != users_.end());
+  const UserCryptohomeState& user_state = user_it->second;
+
+  const bool is_ecryptfs =
+      user_state.home_encryption_method == HomeEncryptionMethod::kEcryptfs;
+  reply.set_needs_dircrypto_migration(is_ecryptfs);
 }
 void FakeUserDataAuthClient::GetSupportedKeyPolicies(
     const ::user_data_auth::GetSupportedKeyPoliciesRequest& request,
@@ -825,15 +887,6 @@
   }
 }
 
-void FakeUserDataAuthClient::SetEcryptfsUserHome(
-    const cryptohome::AccountIdentifier& cryptohome_id,
-    bool use_ecryptfs) {
-  if (use_ecryptfs)
-    ecryptfs_user_homes_.insert(cryptohome_id);
-  else
-    ecryptfs_user_homes_.erase(cryptohome_id);
-}
-
 void FakeUserDataAuthClient::RunPendingWaitForServiceToBeAvailableCallbacks() {
   std::vector<WaitForServiceToBeAvailableCallback> callbacks;
   callbacks.swap(pending_wait_for_service_to_be_available_callbacks_);
@@ -856,7 +909,13 @@
     NotifyDircryptoMigrationProgress(
         ::user_data_auth::DircryptoMigrationStatus::DIRCRYPTO_MIGRATION_SUCCESS,
         dircrypto_migration_progress_, kDircryptoMigrationMaxProgress);
-    SetEcryptfsUserHome(last_migrate_to_dircrypto_request_.account_id(), false);
+    const auto user_it =
+        users_.find(last_migrate_to_dircrypto_request_.account_id());
+    DCHECK(user_it != std::end(users_))
+        << "User for dircrypto migration does not exist";
+
+    UserCryptohomeState& user_state = user_it->second;
+    user_state.home_encryption_method = HomeEncryptionMethod::kDirCrypto;
     dircrypto_migration_progress_timer_.Stop();
     return;
   }
@@ -885,11 +944,6 @@
     observer.DircryptoMigrationProgress(progress);
 }
 
-bool FakeUserDataAuthClient::IsEcryptfsUserHome(
-    const cryptohome::AccountIdentifier& cryptohome_id) {
-  return base::Contains(ecryptfs_user_homes_, cryptohome_id);
-}
-
 void FakeUserDataAuthClient::CreateUserProfileDir(
     const cryptohome::AccountIdentifier& account_id) {
   base::CreateDirectory(GetUserProfileDir(account_id));
@@ -947,7 +1001,8 @@
   // Insert user without any associated keys.
   const auto [_, was_inserted] =
       users_.insert({account_id, UserCryptohomeState()});
-  DCHECK(was_inserted) << "User already exists: " << account_id.account_id();
+  LOG_IF(ERROR, !was_inserted)
+      << "User already exists: " << account_id.account_id();
 }
 
 }  // namespace ash
diff --git a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h
index 148e9b8..e85160f 100644
--- a/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h
+++ b/chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h
@@ -9,8 +9,6 @@
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 
-#include <set>
-
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
@@ -24,6 +22,12 @@
 class COMPONENT_EXPORT(USERDATAAUTH_CLIENT) FakeUserDataAuthClient
     : public UserDataAuthClient {
  public:
+  // The method by which a user's home directory can be encrypted.
+  enum class HomeEncryptionMethod {
+    kDirCrypto,
+    kEcryptfs,
+  };
+
   class COMPONENT_EXPORT(USERDATAAUTH_CLIENT) TestApi {
    public:
     ~TestApi() = default;
@@ -75,8 +79,9 @@
     // Marks |cryptohome_id| as using ecryptfs (|use_ecryptfs|=true) or
     // dircrypto
     // (|use_ecryptfs|=false).
-    void SetEcryptfsUserHome(const cryptohome::AccountIdentifier& cryptohome_id,
-                             bool use_ecryptfs);
+    void SetHomeEncryptionMethod(
+        const cryptohome::AccountIdentifier& cryptohome_id,
+        HomeEncryptionMethod method);
 
     // Marks a PIN key as locked or unlocked. The key is identified by the
     // |account_id| of the user it belongs to and its |label|. The key must
@@ -310,9 +315,6 @@
   void ReturnProtobufMethodCallback(const ReplyType& reply,
                                     DBusMethodCallback<ReplyType> callback);
 
-  // Returns true if the user's home is backed by eCryptfs.
-  bool IsEcryptfsUserHome(const cryptohome::AccountIdentifier& cryptohome_id);
-
   // This method is used to implement StartMigrateToDircrypto with simulated
   // progress updates.
   void OnDircryptoMigrationProgressUpdated();
@@ -333,12 +335,6 @@
 
   void RunPendingWaitForServiceToBeAvailableCallbacks();
 
-  // Marks |cryptohome_id| as using ecryptfs (|use_ecryptfs|=true) or
-  // dircrypto
-  // (|use_ecryptfs|=false).
-  void SetEcryptfsUserHome(const cryptohome::AccountIdentifier& cryptohome_id,
-                           bool use_ecryptfs);
-
   ::user_data_auth::CryptohomeErrorCode cryptohome_error_ =
       ::user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET;
   int prepare_guest_request_count_ = 0;
@@ -351,10 +347,6 @@
   // The collection of users we know about.
   std::map<cryptohome::AccountIdentifier, UserCryptohomeState> users_;
 
-  // Set of account identifiers whose user homes use ecryptfs. User homes not
-  // mentioned here use dircrypto.
-  std::set<cryptohome::AccountIdentifier> ecryptfs_user_homes_;
-
   // Timer for triggering the dircrypto migration progress signal.
   base::RepeatingTimer dircrypto_migration_progress_timer_;
 
diff --git a/chromeos/ash/components/memory/BUILD.gn b/chromeos/ash/components/memory/BUILD.gn
index 93b4be5..b912e8c 100644
--- a/chromeos/ash/components/memory/BUILD.gn
+++ b/chromeos/ash/components/memory/BUILD.gn
@@ -18,6 +18,7 @@
     "//components/memory_pressure",
     "//crypto",
     "//services/resource_coordinator/public/cpp/memory_instrumentation",
+    "//third_party/re2",
   ]
   sources = [
     "aligned_memory.h",
@@ -31,6 +32,12 @@
     "pressure/system_memory_pressure_evaluator.h",
     "swap_configuration.cc",
     "swap_configuration.h",
+    "zram_writeback_backend.cc",
+    "zram_writeback_backend.h",
+    "zram_writeback_controller.cc",
+    "zram_writeback_controller.h",
+    "zram_writeback_policy.cc",
+    "zram_writeback_policy.h",
   ]
 }
 
@@ -50,5 +57,6 @@
     "memory_unittest.cc",
     "pagemap_unittest.cc",
     "pressure/system_memory_pressure_evaluator_unittest.cc",
+    "zram_writeback_controller_unittest.cc",
   ]
 }
diff --git a/chromeos/ash/components/memory/DEPS b/chromeos/ash/components/memory/DEPS
index bac6fbf9..f7828b3 100644
--- a/chromeos/ash/components/memory/DEPS
+++ b/chromeos/ash/components/memory/DEPS
@@ -1,6 +1,7 @@
 # These deps should be only the required deps for //chromeos/ash/components/memory
 include_rules = [
   "+third_party/zlib/google",
+  "+third_party/re2",
   "+services/resource_coordinator",
   "+components/memory_pressure",
   "+content/public/child",
diff --git a/chromeos/ash/components/memory/swap_configuration.cc b/chromeos/ash/components/memory/swap_configuration.cc
index 2296b656..c940dc2 100644
--- a/chromeos/ash/components/memory/swap_configuration.cc
+++ b/chromeos/ash/components/memory/swap_configuration.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/memory/swap_configuration.h"
 
 #include "base/bind.h"
+#include "base/component_export.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -42,6 +43,52 @@
     kCrOSMemoryPressureSignalStudyModerateThresholdPrecentageBps{
         &kCrOSMemoryPressureSignalStudy, "moderate_threshold_percentage", 4000};
 
+COMPONENT_EXPORT(ASH_MEMORY)
+const base::Feature kCrOSEnableZramWriteback{"ChromeOSZramWriteback",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::FeatureParam<int> kCrOSWritebackPeriodicTimeSec{
+    &kCrOSEnableZramWriteback, "ZramWritebackPeriodicTimeSec",
+    base::Seconds(10).InSeconds()};
+const base::FeatureParam<int> kCrOSZramWritebackWritebackBackoffTimeSec{
+    &kCrOSEnableZramWriteback, "CrOSZramWritebackWritebackBackoffTimeSec",
+    base::Seconds(30).InSeconds()};
+const base::FeatureParam<int> kCrOSZramWritebackDevSizePctFree{
+    &kCrOSEnableZramWriteback, "ZramBDSizePctOfFree", 15};
+const base::FeatureParam<int> kCrOSWritebackMinPages{
+    &kCrOSEnableZramWriteback, "ZramWritebackMinPages",
+    ((2 << 20) / 4096) /* 2MiB worth of pages */};
+const base::FeatureParam<int> kCrOSWritebackMaxPages{
+    &kCrOSEnableZramWriteback, "ZramWritebackMaxPages",
+    ((128 << 20) / 4096) /* 128MiB worth of pages */
+};
+const base::FeatureParam<bool> kCrOSWritebackHuge{&kCrOSEnableZramWriteback,
+                                                  "ZramWritebackHuge", false};
+const base::FeatureParam<bool> kCrOSWritebackHugeIdle{
+    &kCrOSEnableZramWriteback, "ZramWritebackHugeIdle", true};
+const base::FeatureParam<bool> kCrOSWritebackIdle{&kCrOSEnableZramWriteback,
+                                                  "ZramWritebackIdle", true};
+const base::FeatureParam<int> kCrOSWritebackIdleMinTimeSec{
+    &kCrOSEnableZramWriteback, "ZramWritebackIdleMinTimeSec",
+    base::Minutes(2).InSeconds()};
+const base::FeatureParam<int> kCrOSWritebackIdleMaxTimeSec{
+    &kCrOSEnableZramWriteback, "ZramWritebackIdleMaxTimeSec",
+    base::Days(1).InSeconds()};
+
+const ZramWritebackParams ZramWritebackParams::Get() {
+  ZramWritebackParams params;
+  params.periodic_time = base::Seconds(kCrOSWritebackPeriodicTimeSec.Get());
+  params.backoff_time =
+      base::Seconds(kCrOSZramWritebackWritebackBackoffTimeSec.Get());
+  params.idle_max_time = base::Seconds(kCrOSWritebackIdleMaxTimeSec.Get());
+  params.idle_min_time = base::Seconds(kCrOSWritebackIdleMinTimeSec.Get());
+  params.max_pages = kCrOSWritebackMaxPages.Get();
+  params.min_pages = kCrOSWritebackMinPages.Get();
+  params.writeback_huge_idle = kCrOSWritebackHugeIdle.Get();
+  params.writeback_huge = kCrOSWritebackHuge.Get();
+  params.writeback_idle = kCrOSWritebackIdle.Get();
+  return params;
+}
+
 namespace {
 
 constexpr const char kMinFilelist[] = "min_filelist";
diff --git a/chromeos/ash/components/memory/swap_configuration.h b/chromeos/ash/components/memory/swap_configuration.h
index 5c12b8b..1e0c4fc5 100644
--- a/chromeos/ash/components/memory/swap_configuration.h
+++ b/chromeos/ash/components/memory/swap_configuration.h
@@ -29,6 +29,64 @@
 extern const base::Feature kCrOSTuneExtraFree;
 extern const base::FeatureParam<int> kCrOSExtraFreeMb;
 
+// This feature and params control the zram writeback behavior.
+extern const base::Feature kCrOSEnableZramWriteback;
+
+// Controls the period in which the controller will check to see if we can write
+// back. It does not guarantee a writeback will actually happen.
+extern const base::FeatureParam<int> kCrOSWritebackPeriodicTimeSec;
+
+// This is the minimum amount of time allowed required writebacks, regardless of
+// system state.
+extern const base::FeatureParam<int> kCrOSZramWritebackWritebackBackoffTimeSec;
+
+// This is the percentage of free space to use on the stateful partition for the
+// backing device.
+extern const base::FeatureParam<int> kCrOSZramWritebackDevSizePctFree;
+
+// This is the absolute minimum number of pages we would consider writing back.
+// That is, the policy will never return a value smaller than this.
+extern const base::FeatureParam<int> kCrOSWritebackMinPages;
+
+// This is the maximum number of pages the policy will allow to be written back
+// in a single period.
+extern const base::FeatureParam<int> kCrOSWritebackMaxPages;
+
+// If enabled we will write back pages as "Huge Idle." Huge idle pages are pages
+// which have been idle for the idle time AND are huge (meaning incompressible).
+extern const base::FeatureParam<bool> kCrOSWritebackHugeIdle;
+
+// If enabled we will writeback idle pages, which are pages which have been in
+// zram for an amount of time greater than the idle time.
+extern const base::FeatureParam<bool> kCrOSWritebackIdle;
+
+// If enabled we will writeback huge pages, these are pages which are
+// incompressible with no requirement on the amount of time they have been
+// inzram.
+extern const base::FeatureParam<bool> kCrOSWritebackHuge;
+
+// This is the hard lower bound on zram idle and huge idle writeback modes idle
+// time. This will create a lower limit on how long a page MUST be in zram
+// before it can be written back.
+extern const base::FeatureParam<int> kCrOSWritebackIdleMinTimeSec;
+
+// This is the hard upper bound on the idle time. That is, the policy will never
+// return a value larger than this for the idle time.
+extern const base::FeatureParam<int> kCrOSWritebackIdleMaxTimeSec;
+
+struct ZramWritebackParams {
+  base::TimeDelta periodic_time;
+  base::TimeDelta backoff_time;
+  uint64_t min_pages;
+  uint64_t max_pages;
+  bool writeback_huge_idle;
+  bool writeback_idle;
+  bool writeback_huge;
+  base::TimeDelta idle_min_time;
+  base::TimeDelta idle_max_time;
+  static const ZramWritebackParams Get();
+};
+
 // Configure swap will configure any swap related experiments that this user may
 // be opted into.
 COMPONENT_EXPORT(ASH_MEMORY) void ConfigureSwap();
diff --git a/chromeos/ash/components/memory/zram_writeback_backend.cc b/chromeos/ash/components/memory/zram_writeback_backend.cc
new file mode 100644
index 0000000..dcdca33
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_backend.cc
@@ -0,0 +1,279 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/memory/zram_writeback_backend.h"
+
+#include <cstdint>
+#include <limits>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/page_size.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/system/sys_info.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace ash::memory {
+
+namespace {
+
+constexpr char kSavedWritebackSizeFile[] = "/tmp/saved-writeback-size";
+constexpr char kZramDev[] = "/sys/block/zram0";
+constexpr char kZramBackingDevFile[] = "backing_dev";
+constexpr char kZramDiskSize[] = "disksize";
+constexpr char kZramWritebackLimitFile[] = "writeback_limit";
+
+int64_t ReadFileAsInt64(const base::FilePath& path) {
+  std::string str;
+  if (!base::ReadFileToStringNonBlocking(path, &str)) {
+    return std::numeric_limits<int64_t>::min();
+  }
+
+  if (!str.empty() && str.back() == '\n') {
+    str.resize(str.size() - 1);
+  }
+
+  int64_t val;
+  if (!base::StringToInt64(str, &val)) {
+    return std::numeric_limits<int64_t>::min();
+  }
+
+  return val;
+}
+
+void SaveWritebackSize(uint64_t size_mb) {
+  const base::FilePath file_path(kSavedWritebackSizeFile);
+  DCHECK(!base::PathExists(file_path));
+  if (base::WriteFile(file_path, base::NumberToString(size_mb))) {
+    base::SetPosixFilePermissions(
+        file_path, base::FilePermissionBits::FILE_PERMISSION_READ_BY_USER);
+  }
+}
+
+void ReadWritebackSize(int64_t* v) {
+  DCHECK(v);
+  const base::FilePath file_path(kSavedWritebackSizeFile);
+  *v = ReadFileAsInt64(file_path);
+}
+
+// The backend is responsible for the "how" things happens.
+class ZramWritebackBackendImpl : public ZramWritebackBackend {
+ public:
+  ~ZramWritebackBackendImpl() override = default;
+
+  void OnEnableWritebackResponse(IntCallback cb,
+                                 int64_t size_mb,
+                                 absl::optional<std::string> res) {
+    if (!res) {
+      // If we did not receive a response, it's likely that debugd is not up
+      // yet. Let's try again in 30 seconds.
+      base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(&ZramWritebackBackendImpl::EnableWriteback,
+                         weak_factory_.GetWeakPtr(), size_mb, std::move(cb)),
+          base::Seconds(30));
+      return;
+    }
+
+    /* let's parse the response */
+    std::string resp = *res;
+    int size;
+    if (RE2::PartialMatch(resp, "SUCCESS: Enabled writeback with size (\\d+)MB",
+                          &size)) {
+      base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()},
+                                 base::BindOnce(&SaveWritebackSize, size));
+
+      std::move(cb).Run(true, size);
+      return;
+    } else {
+      if (resp.find("ERROR") != std::string::npos) {
+        LOG(ERROR) << "Error configuring zram writeback: " << resp;
+      } else {
+        LOG(WARNING) << "Unexpected response from debugd: " << resp;
+      }
+    }
+
+    std::move(cb).Run(false, 0);
+  }
+
+  void EnableWriteback(uint64_t size_mb, IntCallback cb) override {
+    chromeos::DebugDaemonClient* debugd_client =
+        chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+    CHECK(debugd_client);
+
+    debugd_client->SwapZramEnableWriteback(
+        size_mb,
+        base::BindOnce(&ZramWritebackBackendImpl::OnEnableWritebackResponse,
+                       weak_factory_.GetWeakPtr(), std::move(cb), size_mb));
+  }
+
+  void OnMarkIdle(Callback cb, absl::optional<std::string> resp) {
+    if (!resp) {
+      std::move(cb).Run(false);
+      return;
+    }
+
+    if ((*resp).find("SUCCESS") == std::string::npos) {
+      LOG(ERROR) << "Zram mark idle returned: " << *resp;
+      std::move(cb).Run(false);
+      return;
+    }
+
+    std::move(cb).Run(true);
+  }
+
+  void MarkIdle(base::TimeDelta age, Callback cb) override {
+    chromeos::DebugDaemonClient* debugd_client =
+        chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+    CHECK(debugd_client);
+
+    debugd_client->SwapZramMarkIdle(
+        age.InSeconds(),
+        base::BindOnce(&ZramWritebackBackendImpl::OnMarkIdle,
+                       weak_factory_.GetWeakPtr(), std::move(cb)));
+  }
+
+  int64_t GetZramDiskSizeBytes() override {
+    return ReadFileAsInt64(kZramPath.Append(kZramDiskSize));
+  }
+
+  int64_t GetCurrentWritebackLimitPages() override {
+    return ReadFileAsInt64(kZramPath.Append(kZramWritebackLimitFile));
+  }
+
+  void OnSetWritebackLimitResponse(IntCallback cb,
+                                   absl::optional<std::string> resp) {
+    if (!resp) {
+      std::move(cb).Run(false, 0);
+      return;
+    }
+
+    if ((*resp).find("SUCCESS") == std::string::npos) {
+      LOG(ERROR) << "Zram Set Writeback Limit returned: " << *resp;
+      std::move(cb).Run(false, 0);
+      return;
+    }
+
+    std::move(cb).Run(true, GetCurrentWritebackLimitPages());
+  }
+
+  void SetWritebackLimit(uint64_t size_pages, IntCallback cb) override {
+    chromeos::DebugDaemonClient* debugd_client =
+        chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+    CHECK(debugd_client);
+
+    debugd_client->SwapZramSetWritebackLimit(
+        size_pages,
+        base::BindOnce(&ZramWritebackBackendImpl::OnSetWritebackLimitResponse,
+                       weak_factory_.GetWeakPtr(), std::move(cb)));
+  }
+
+  void OnInitiateWritebackResponse(Callback cb,
+                                   absl::optional<std::string> resp) {
+    if (!resp) {
+      std::move(cb).Run(false);
+      return;
+    }
+
+    if ((*resp).find("SUCCESS") == std::string::npos) {
+      // I/O Errors (-EIO) result when we've hit our writeback limit, and thus
+      // are not considered an error, although we won't return true we will not
+      // log an error here.
+      bool io_error =
+          ((*resp).find("Error 5 (Input/output error)") != std::string::npos);
+      LOG_IF(ERROR, !io_error) << "Zram initiate writeback returned: " << *resp;
+      std::move(cb).Run(false);
+      return;
+    }
+
+    std::move(cb).Run(true);
+  }
+
+  void InitiateWriteback(ZramWritebackMode mode, Callback cb) override {
+    chromeos::DebugDaemonClient* debugd_client =
+        chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+    CHECK(debugd_client);
+
+    debugd_client->InitiateSwapZramWriteback(
+        static_cast<debugd::ZramWritebackMode>(mode),
+        base::BindOnce(&ZramWritebackBackendImpl::OnInitiateWritebackResponse,
+                       weak_factory_.GetWeakPtr(), std::move(cb)));
+  }
+
+  bool WritebackAlreadyEnabled() override {
+    std::string contents;
+    if (base::ReadFileToStringNonBlocking(kZramPath.Append(kZramBackingDevFile),
+                                          &contents)) {
+      const std::string kNone("none");
+      // For whatever reason the backing file in zram will tack on a \n, let's
+      // remove it.
+      if (!contents.empty() && contents.at(contents.length() - 1) == '\n')
+        contents = contents.substr(0, contents.length() - 1);
+      if (contents != kNone) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  void GetCurrentBackingDevSize(IntCallback cb) override {
+    // We need to read on the thread pool and then reply back to the callback.
+    int64_t* val = new int64_t(-1);
+    base::ThreadPool::PostTaskAndReply(
+        FROM_HERE, {base::MayBlock()}, base::BindOnce(&ReadWritebackSize, val),
+        base::BindOnce(
+            [](decltype(cb) cb, decltype(val) v) {
+              std::move(cb).Run(*v != std::numeric_limits<int64_t>::min(), *v);
+            },
+            std::move(cb), base::Owned(val)));
+  }
+
+  static const base::FilePath kZramPath;
+
+ private:
+  base::WeakPtrFactory<ZramWritebackBackendImpl> weak_factory_{this};
+};
+
+const base::FilePath ZramWritebackBackendImpl::kZramPath =
+    base::FilePath(kZramDev);
+
+}  // namespace
+
+// static
+std::unique_ptr<ZramWritebackBackend> COMPONENT_EXPORT(ASH_MEMORY)
+    ZramWritebackBackend::Get() {
+  return std::make_unique<ZramWritebackBackendImpl>();
+}
+
+// static
+bool COMPONENT_EXPORT(ASH_MEMORY) ZramWritebackBackend::IsSupported() {
+  // zram writeback is only on CrOS 5.x+ kernels.
+  int32_t major_version = 0;
+  int32_t minor_version = 0;
+  int32_t micro_version = 0;
+  base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
+                                               &micro_version);
+  if (major_version < 5) {
+    return false;
+  }
+
+  // Was the kernel built with writeback support?
+  base::FilePath backing_dev =
+      ZramWritebackBackendImpl::kZramPath.Append(kZramBackingDevFile);
+  if (!base::PathExists(backing_dev)) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace ash::memory
diff --git a/chromeos/ash/components/memory/zram_writeback_backend.h b/chromeos/ash/components/memory/zram_writeback_backend.h
new file mode 100644
index 0000000..905c0cb
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_backend.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_BACKEND_H_
+#define CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_BACKEND_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+
+namespace ash::memory {
+
+enum class ZramWritebackMode : int {
+  kModeNone = 0,
+  kModeIdle = 1,
+  kModeHuge = 2,
+  kModeHugeIdle = 4
+};
+
+// The backend is responsible for the "how" things happens.
+class COMPONENT_EXPORT(ASH_MEMORY) ZramWritebackBackend {
+ public:
+  virtual ~ZramWritebackBackend() = default;
+
+  using IntCallback = base::OnceCallback<void(bool, int64_t)>;
+  virtual void EnableWriteback(uint64_t size_mb, IntCallback cb) = 0;
+  virtual void SetWritebackLimit(uint64_t size_pages, IntCallback cb) = 0;
+
+  using Callback = base::OnceCallback<void(bool)>;
+  virtual void InitiateWriteback(ZramWritebackMode mode, Callback cb) = 0;
+  virtual void MarkIdle(base::TimeDelta age, Callback cb) = 0;
+  virtual bool WritebackAlreadyEnabled() = 0;
+
+  // If and only if writeback already enabled returns true, then
+  // GetPreviousBackingSize will return the size (in MB) of the backing
+  // device that's currently configured.
+  virtual void GetCurrentBackingDevSize(IntCallback cb) = 0;
+
+  // Returns the value currently stored, this can be read AFTER an initiate
+  // writeback to determine how many pages were written back. This will
+  // return -1 if there is no limit set.
+  virtual int64_t GetCurrentWritebackLimitPages() = 0;
+  virtual int64_t GetZramDiskSizeBytes() = 0;
+
+  static std::unique_ptr<ZramWritebackBackend> Get();
+  static bool IsSupported();
+};
+}  // namespace ash::memory
+
+#endif  // CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_BACKEND_H_
diff --git a/chromeos/ash/components/memory/zram_writeback_controller.cc b/chromeos/ash/components/memory/zram_writeback_controller.cc
new file mode 100644
index 0000000..a59885c
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_controller.cc
@@ -0,0 +1,223 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/memory/zram_writeback_controller.h"
+
+#include <algorithm>
+#include <cstdint>
+
+#include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/page_size.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/thread_pool.h"
+#include "base/time/time.h"
+#include "chromeos/ash/components/memory/memory.h"
+#include "chromeos/ash/components/memory/swap_configuration.h"
+#include "chromeos/ash/components/memory/zram_writeback_backend.h"
+#include "chromeos/ash/components/memory/zram_writeback_policy.h"
+
+namespace ash::memory {
+
+// The ZramWritebackController handles zram writeback. For more information on
+// how zram writeback works, see the kernel documentation:
+// https://www.kernel.org/doc/Documentation/admin-guide/blockdev/zram.rst,
+// specifically the section on writeback.
+//
+// Zram Writeback involves a multistep process which is coordinated by the
+// controller. The process is as follows:
+//
+// 1. The WritebackController registers a timer event which is dictated by the
+//    policy, this value comes from GetWritebackTimerInterval(). The timer will
+//    fire into PeriodicWriteback.
+// 2. On a periodic writeback event the controller will query the policy to
+//    determine the number of pages allowed to be written back. This value might
+//    be zero if the policy has determined that writeback should not happen
+// 3. If the policy provides a non-zero writeback limit, this value will be set
+//    via the backend by a call to SetWritebackLimit(). The callback will land
+//    in OnSetWritebackLimit.
+// 4. In OnSetWritebackLimit we will query the policy to determine if we will be
+//    writing back any idle pages. This will happen via calls to
+//    CanWriteback(HugeIdle|Idle). If either is true, the policy will be queried
+//    to determine the idle age by a call to GetCurrentWritebackIdleTime(). This
+//    value will then be passed to the backend to be set in the kernel sysfs
+//    file.
+// 5. At this point writeback will begin by calls to the backend
+//    InitiateWriteback() with the writeback types allowed, the ordering (if
+//    enabled) will be: Huge Idle, Idle, Huge. If after any of these writebacks
+//    the current writeback limit falls to zero, which is determined by calling
+//    the backend's GetCurrentWritebackLimit() method) we stop without
+//    proceeding on to the next type.
+ZramWritebackController::ZramWritebackController(
+    std::unique_ptr<ZramWritebackPolicy> policy,
+    std::unique_ptr<ZramWritebackBackend> backend)
+    : policy_(std::move(policy)), backend_(std::move(backend)) {}
+
+// static
+std::unique_ptr<ZramWritebackController> ZramWritebackController::Create() {
+  return base::WrapUnique(new ZramWritebackController(
+      ZramWritebackPolicy::Get(), ZramWritebackBackend::Get()));
+}
+
+ZramWritebackController::~ZramWritebackController() = default;
+
+void ZramWritebackController::OnEnableWriteback(bool result, int64_t size_mb) {
+  if (!result) {
+    LOG(ERROR) << "Unable to enable zram writeback";
+    return;
+  }
+
+  // Finally we can complete the policy initialization.
+  CompleteInitialization(backend_->GetZramDiskSizeBytes() >> 20, size_mb);
+}
+
+void ZramWritebackController::ResetCurrentlyWritingBack() {
+  current_writeback_mode_ = ZramWritebackMode::kModeNone;
+  currently_writing_back_ = false;
+  current_writeback_limit_ = 0;
+}
+
+void ZramWritebackController::CompleteInitialization(
+    uint64_t zram_size_mb,
+    uint64_t writeback_size_mb) {
+  policy_->Initialize(zram_size_mb, writeback_size_mb);
+  timer_.Start(FROM_HERE, policy_->GetWritebackTimerInterval(),
+               base::BindRepeating(&ZramWritebackController::PeriodicWriteback,
+                                   weak_factory_.GetWeakPtr()));
+}
+
+// static
+bool ZramWritebackController::IsSupportedAndEnabled() {
+  return ZramWritebackBackend::IsSupported() &&
+         base::FeatureList::IsEnabled(kCrOSEnableZramWriteback);
+}
+
+void ZramWritebackController::Stop() {
+  timer_.Stop();
+}
+
+void ZramWritebackController::Start() {
+  if (backend_->WritebackAlreadyEnabled()) {
+    // The fact that it was already enabled is fine, we just need to query the
+    // size of the writeback device and then we can initialize the parameters of
+    // the policy.
+    backend_->GetCurrentBackingDevSize(
+        base::BindOnce(&ZramWritebackController::OnEnableWriteback,
+                       weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  backend_->EnableWriteback(
+      1024, base::BindOnce(&ZramWritebackController::OnEnableWriteback,
+                           weak_factory_.GetWeakPtr()));
+}
+
+void ZramWritebackController::OnWritebackComplete(ZramWritebackMode mode,
+                                                  bool result) {
+  if (!result) {
+    ResetCurrentlyWritingBack();
+    return;
+  }
+
+  // Now let's figure out how much work we actually did, even in the failure
+  // case we might write back some pages.
+  uint64_t cur_limit = backend_->GetCurrentWritebackLimitPages();
+  current_writeback_limit_ = cur_limit;
+  if (current_writeback_limit_ == 0) {
+    ResetCurrentlyWritingBack();
+    return;
+  }
+
+  // Move on to the next writeback phase.
+  if (current_writeback_mode_ == ZramWritebackMode::kModeHugeIdle &&
+      policy_->CanWritebackIdle())
+    current_writeback_mode_ = ZramWritebackMode::kModeIdle;
+  else if ((current_writeback_mode_ == ZramWritebackMode::kModeIdle ||
+            current_writeback_mode_ == ZramWritebackMode::kModeHugeIdle) &&
+           policy_->CanWritebackHuge()) {
+    current_writeback_mode_ = ZramWritebackMode::kModeHuge;
+  } else {
+    // We're done.
+    ResetCurrentlyWritingBack();
+    return;
+  }
+
+  ReadyToWriteback();
+}
+
+void ZramWritebackController::ReadyToWriteback() {
+  backend_->InitiateWriteback(
+      current_writeback_mode_,
+      base::BindOnce(&ZramWritebackController::OnWritebackComplete,
+                     weak_factory_.GetWeakPtr(), current_writeback_mode_));
+}
+
+void ZramWritebackController::OnMarkIdle(bool result) {
+  if (!result) {
+    LOG(ERROR) << "Failed to mark pages as idle";
+    ResetCurrentlyWritingBack();
+    return;
+  }
+
+  current_writeback_mode_ = policy_->CanWritebackHugeIdle()
+                                ? ZramWritebackMode::kModeHugeIdle
+                                : ZramWritebackMode::kModeIdle;
+  ReadyToWriteback();
+}
+
+void ZramWritebackController::OnSetWritebackLimit(bool result,
+                                                  int64_t number_pages) {
+  if (!result || !number_pages) {
+    LOG(ERROR) << "Failed to set writeback limit";
+    ResetCurrentlyWritingBack();
+    return;
+  }
+
+  current_writeback_limit_ = number_pages;
+
+  // We need to start by doing an idle mark sweep (if enabled).
+  if (policy_->CanWritebackHugeIdle() || policy_->CanWritebackIdle()) {
+    base::TimeDelta idle_age = policy_->GetCurrentWritebackIdleTime();
+    if (idle_age == base::TimeDelta::Max()) {
+      ResetCurrentlyWritingBack();
+      return;
+    }
+
+    backend_->MarkIdle(idle_age,
+                       base::BindOnce(&ZramWritebackController::OnMarkIdle,
+                                      weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  // If we do not allow writing back idle or huge the policy should have never
+  // given us a page limit anyway.
+  DCHECK(policy_->CanWritebackHuge());
+
+  // Since we can't writeback idle, we start writing back huge pages.
+  current_writeback_mode_ = ZramWritebackMode::kModeHuge;
+  ReadyToWriteback();
+}
+
+void ZramWritebackController::PeriodicWriteback() {
+  if (currently_writing_back_) {
+    return;
+  }
+
+  currently_writing_back_ = true;
+  current_writeback_limit_ = policy_->GetAllowedWritebackLimit();
+  if (!current_writeback_limit_) {
+    ResetCurrentlyWritingBack();
+    return;
+  }
+
+  backend_->SetWritebackLimit(
+      current_writeback_limit_,
+      base::BindOnce(&ZramWritebackController::OnSetWritebackLimit,
+                     weak_factory_.GetWeakPtr()));
+}
+
+}  // namespace ash::memory
diff --git a/chromeos/ash/components/memory/zram_writeback_controller.h b/chromeos/ash/components/memory/zram_writeback_controller.h
new file mode 100644
index 0000000..4b82493
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_controller.h
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_CONTROLLER_H_
+#define CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_CONTROLLER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chromeos/ash/components/memory/zram_writeback_backend.h"
+#include "chromeos/ash/components/memory/zram_writeback_policy.h"
+
+namespace ash::memory {
+
+// The controller bridges the policy and backend.
+class COMPONENT_EXPORT(ASH_MEMORY) ZramWritebackController {
+ public:
+  ~ZramWritebackController();
+  ZramWritebackController(const ZramWritebackController&) = delete;
+  ZramWritebackController& operator=(const ZramWritebackController&) = delete;
+
+  static std::unique_ptr<ZramWritebackController> Create();
+  static bool IsSupportedAndEnabled();
+  void Start();
+  void Stop();
+
+ private:
+  friend class ZramWritebackControllerTest;
+  ZramWritebackController(std::unique_ptr<ZramWritebackPolicy> policy,
+                          std::unique_ptr<ZramWritebackBackend> backend);
+
+  void PeriodicWriteback();
+  void ReadyToWriteback();
+  void OnEnableWriteback(bool result, int64_t writeback_size_mb);
+  void OnSetWritebackLimit(bool result, int64_t num_pages);
+  void CompleteInitialization(uint64_t zram_size_mb,
+                              uint64_t writeback_size_mb);
+  void OnWritebackComplete(ZramWritebackMode mode, bool result);
+  void OnMarkIdle(bool result);
+  void ResetCurrentlyWritingBack();
+
+  base::RepeatingTimer timer_;
+  bool currently_writing_back_ = false;
+  uint64_t current_writeback_limit_ = 0;
+  ZramWritebackMode current_writeback_mode_ = ZramWritebackMode::kModeNone;
+
+  std::unique_ptr<ZramWritebackPolicy> policy_;
+  std::unique_ptr<ZramWritebackBackend> backend_;
+
+  base::WeakPtrFactory<ZramWritebackController> weak_factory_{this};
+};
+
+}  // namespace ash::memory
+
+#endif  // CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_CONTROLLER_H_
diff --git a/chromeos/ash/components/memory/zram_writeback_controller_unittest.cc b/chromeos/ash/components/memory/zram_writeback_controller_unittest.cc
new file mode 100644
index 0000000..61505ce
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_controller_unittest.cc
@@ -0,0 +1,301 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/memory/zram_writeback_controller.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "chromeos/ash/components/memory/zram_writeback_backend.h"
+#include "chromeos/ash/components/memory/zram_writeback_policy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash::memory {
+
+namespace {
+using testing::_;
+using testing::ByRef;
+using testing::Eq;
+using testing::Exactly;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+constexpr int kMbShift = 20;
+}  // namespace
+
+class MockZramWritebackBackend : public ZramWritebackBackend {
+ public:
+  MockZramWritebackBackend() = default;
+  MockZramWritebackBackend(const MockZramWritebackBackend&) = delete;
+  MockZramWritebackBackend& operator=(const MockZramWritebackBackend&) = delete;
+
+  ~MockZramWritebackBackend() override = default;
+
+  MOCK_METHOD2(EnableWriteback, void(uint64_t size_mb, IntCallback cb));
+  MOCK_METHOD2(SetWritebackLimit, void(uint64_t size_pages, IntCallback cb));
+  MOCK_METHOD2(InitiateWriteback, void(ZramWritebackMode mode, Callback cb));
+  MOCK_METHOD2(MarkIdle, void(base::TimeDelta age, Callback cb));
+  MOCK_METHOD0(WritebackAlreadyEnabled, bool());
+
+  MOCK_METHOD1(GetCurrentBackingDevSize, void(IntCallback cb));
+  MOCK_METHOD0(GetZramDiskSizeBytes, int64_t());
+  MOCK_METHOD0(GetCurrentWritebackLimitPages, int64_t());
+  MOCK_METHOD0(GetCurrentBackingDevSize, int64_t());
+};
+
+class MockZramWritebackPolicy : public ZramWritebackPolicy {
+ public:
+  MockZramWritebackPolicy() = default;
+  MockZramWritebackPolicy(const MockZramWritebackPolicy&) = delete;
+  MockZramWritebackPolicy& operator=(const MockZramWritebackPolicy&) = delete;
+
+  ~MockZramWritebackPolicy() override = default;
+
+  MOCK_METHOD2(Initialize,
+               void(uint64_t zram_disk_size_mb, uint64_t writeback_size_mb));
+
+  MOCK_METHOD0(CanWritebackHugeIdle, bool());
+  MOCK_METHOD0(CanWritebackHuge, bool());
+  MOCK_METHOD0(CanWritebackIdle, bool());
+  MOCK_METHOD0(GetCurrentWritebackIdleTime, base::TimeDelta());
+  MOCK_METHOD0(GetAllowedWritebackLimit, uint64_t());
+  MOCK_METHOD0(GetWritebackTimerInterval, base::TimeDelta());
+};
+
+class ZramWritebackControllerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    std::unique_ptr<MockZramWritebackPolicy> policy;
+    std::unique_ptr<MockZramWritebackBackend> backend;
+    CreatePolicyAndBackend(&policy, &backend);
+
+    // Save for mocks.
+    policy_ = policy.get();
+    backend_ = backend.get();
+    controller_ = base::WrapUnique(
+        new ZramWritebackController(std::move(policy), std::move(backend)));
+  }
+
+  void TearDown() override {
+    if (controller_)
+      controller_->Stop();
+  }
+
+  // By default we create a nice mock, there is a similar StrictMock below.
+  virtual void CreatePolicyAndBackend(
+      std::unique_ptr<MockZramWritebackPolicy>* policy,
+      std::unique_ptr<MockZramWritebackBackend>* backend) {
+    *policy = std::make_unique<NiceMock<MockZramWritebackPolicy>>();
+    *backend = std::make_unique<NiceMock<MockZramWritebackBackend>>();
+  }
+
+ protected:
+  using Callback = MockZramWritebackBackend::Callback;
+  using IntCallback = MockZramWritebackBackend::IntCallback;
+
+  MockZramWritebackPolicy* policy() { return policy_; }
+  MockZramWritebackBackend* backend() { return backend_; }
+  ZramWritebackController* controller() { return controller_.get(); }
+  base::test::TaskEnvironment* task_env() { return &task_environment_; };
+
+ private:
+  // Capture only for the mock.
+  MockZramWritebackPolicy* policy_;
+  MockZramWritebackBackend* backend_;
+
+  std::unique_ptr<ZramWritebackController> controller_;
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+};
+
+TEST_F(ZramWritebackControllerTest, TestInitializationSequence) {
+  constexpr uint64_t kWbSize = 555;
+  constexpr uint64_t kZramSizeMb = 1000;
+  EXPECT_CALL(*backend(), WritebackAlreadyEnabled()).WillOnce(Return(false));
+  EXPECT_CALL(*backend(), EnableWriteback(_, _))
+      .WillOnce(Invoke([](uint64_t size, IntCallback cb) {
+        std::move(cb).Run(true, kWbSize);
+      }));
+  EXPECT_CALL(*backend(), GetZramDiskSizeBytes())
+      .WillRepeatedly(Return(kZramSizeMb << kMbShift));
+  EXPECT_CALL(*policy(), Initialize(kZramSizeMb, kWbSize)).Times(1);
+
+  controller()->Start();
+}
+
+TEST_F(ZramWritebackControllerTest, TestInitializationSequenceAlreadyEnabled) {
+  constexpr uint64_t kWbSize = 555;
+  constexpr uint64_t kZramSizeMb = 1000;
+  EXPECT_CALL(*backend(), WritebackAlreadyEnabled()).WillOnce(Return(true));
+  EXPECT_CALL(*backend(), EnableWriteback(_, _)).Times(0);
+  EXPECT_CALL(*backend(), GetCurrentBackingDevSize(_))
+      .WillOnce(
+          Invoke([](IntCallback cb) { std::move(cb).Run(true, kWbSize); }));
+
+  EXPECT_CALL(*backend(), GetZramDiskSizeBytes())
+      .WillRepeatedly(Return(kZramSizeMb << kMbShift));
+  EXPECT_CALL(*policy(), Initialize(kZramSizeMb, kWbSize)).Times(1);
+
+  controller()->Start();
+}
+
+// SimpleStrictZramWritebackController mocks out methods to bring up a simple
+// controller removing the boiler plate and allowing tests to just focus on one
+// piece.
+class SimpleStrictZramWritebackControllerTest
+    : public ZramWritebackControllerTest {
+ public:
+  ~SimpleStrictZramWritebackControllerTest() override = default;
+
+  void SetUp() override {
+    ZramWritebackControllerTest::SetUp();
+
+    // Do a basic setup with standard values.
+    EXPECT_CALL(*backend(), WritebackAlreadyEnabled()).WillOnce(Return(false));
+    EXPECT_CALL(*backend(), EnableWriteback(_, _))
+        .WillOnce(Invoke([](uint64_t size, IntCallback cb) {
+          std::move(cb).Run(true, kWbSize);
+        }));
+    EXPECT_CALL(*backend(), GetZramDiskSizeBytes())
+        .WillRepeatedly(Return(kZramSizeMb << kMbShift));
+    EXPECT_CALL(*policy(), Initialize(kZramSizeMb, kWbSize)).Times(1);
+  }
+
+  void CreatePolicyAndBackend(
+      std::unique_ptr<MockZramWritebackPolicy>* policy,
+      std::unique_ptr<MockZramWritebackBackend>* backend) override {
+    *policy = std::make_unique<StrictMock<MockZramWritebackPolicy>>();
+    *backend = std::make_unique<StrictMock<MockZramWritebackBackend>>();
+  }
+
+  void TearDown() override { ZramWritebackControllerTest::TearDown(); }
+
+ protected:
+  static constexpr uint64_t kWbSize = 555;
+  static constexpr uint64_t kZramSizeMb = 1000;
+};
+
+TEST_F(SimpleStrictZramWritebackControllerTest, TestPeriodicWritebackTimer) {
+  EXPECT_CALL(*policy(), GetWritebackTimerInterval())
+      .WillOnce(Return(base::Seconds(1)));
+
+  // Validate that our timer is fired at the appropriate interval.
+  EXPECT_CALL(*policy(), GetAllowedWritebackLimit())
+      .Times(10)
+      .WillRepeatedly(Return(0));
+
+  controller()->Start();
+
+  base::RunLoop run_loop;
+
+  // If we run for 10 seconds we should check our writeback limit 10 times.
+  task_env()->FastForwardBy(base::Seconds(10));
+  run_loop.RunUntilIdle();
+}
+
+TEST_F(SimpleStrictZramWritebackControllerTest, TestFailSetWritebackLimit) {
+  EXPECT_CALL(*policy(), GetWritebackTimerInterval())
+      .WillOnce(Return(base::Seconds(1)));
+
+  constexpr uint64_t kPageLimit = 15;
+  EXPECT_CALL(*policy(), GetAllowedWritebackLimit())
+      .Times(1)
+      .WillRepeatedly(Return(kPageLimit));
+
+  // Ultimately the controller needs to set the limit on the system via the
+  // backend.
+  EXPECT_CALL(*backend(), SetWritebackLimit(_, _))
+      .Times(1)
+      .WillRepeatedly(Invoke(
+          [](uint64_t limit, IntCallback cb) { std::move(cb).Run(false, 1); }));
+  // We invoked the SetWritebackLimit callback with false (failed), so no
+  // further methods should be called, we're a strict mock so that's how we
+  // confirm this.
+
+  controller()->Start();
+  base::RunLoop run_loop;
+  task_env()->FastForwardBy(base::Seconds(1));
+  run_loop.RunUntilIdle();
+}
+
+// AdvancedStrictZramWritebackController test builds on the simple variant,
+// allowing us to test more intriciate scenarios with less boiler plate code.
+class AdvancedStrictZramWritebackControllerTest
+    : public SimpleStrictZramWritebackControllerTest {
+ public:
+  ~AdvancedStrictZramWritebackControllerTest() override = default;
+
+  void SetUp() override { SimpleStrictZramWritebackControllerTest::SetUp(); }
+  void TearDown() override {
+    SimpleStrictZramWritebackControllerTest::TearDown();
+  }
+
+  void RunTest() {
+    task_env()->FastForwardBy(times_ * period_);
+    run_loop_.RunUntilIdle();
+  }
+
+  void SetTestConfig(base::TimeDelta period, int times) {
+    period_ = period;
+    times_ = times;
+  }
+
+ protected:
+  static constexpr uint64_t kPageLimit = 15;
+  base::RunLoop run_loop_;
+  int times_;
+  base::TimeDelta period_;
+};
+
+TEST_F(AdvancedStrictZramWritebackControllerTest,
+       IdleWritebackDoesIdleMarkAndWb) {
+  // This test is validating that when we've enabled idle writeback we're doing
+  // an idle sweep with the calculated age.
+  SetTestConfig(/*period=*/base::Seconds(1), /*times=*/1);
+  EXPECT_CALL(*policy(), GetWritebackTimerInterval()).WillOnce(Return(period_));
+
+  EXPECT_CALL(*policy(), GetAllowedWritebackLimit())
+      .Times(times_)
+      .WillRepeatedly(Return(kPageLimit));
+
+  EXPECT_CALL(*backend(), SetWritebackLimit(_, _))
+      .Times(times_)
+      .WillRepeatedly(Invoke([](uint64_t limit, IntCallback cb) {
+        std::move(cb).Run(true, kPageLimit);
+      }));
+  EXPECT_CALL(*policy(), CanWritebackIdle()).WillOnce(Return(true));
+
+  // The callback will check our current state, so it checks huge idle one more
+  // time.
+  EXPECT_CALL(*policy(), CanWritebackHugeIdle())
+      .Times(2)
+      .WillRepeatedly(Return(false));
+
+  EXPECT_CALL(*policy(), GetCurrentWritebackIdleTime())
+      .WillOnce(Return(base::Seconds(60)));
+
+  // We expect to do an idle sweep of 60s.
+  EXPECT_CALL(*backend(), MarkIdle(base::Seconds(60), _))
+      .WillOnce(Invoke(
+          [](base::TimeDelta age, Callback cb) { std::move(cb).Run(true); }));
+
+  // We expect to now initiate a writeback for idle.
+  EXPECT_CALL(*backend(), InitiateWriteback(ZramWritebackMode::kModeIdle, _))
+      .WillOnce(Invoke([](ZramWritebackMode mode, Callback cb) {
+        std::move(cb).Run(true);
+      }));
+
+  // Finally we will report that our writeback limit is 0 (all pages were
+  // written back).
+  EXPECT_CALL(*backend(), GetCurrentWritebackLimitPages()).WillOnce(Return(0));
+
+  controller()->Start();
+  RunTest();
+}
+
+}  // namespace ash::memory
diff --git a/chromeos/ash/components/memory/zram_writeback_policy.cc b/chromeos/ash/components/memory/zram_writeback_policy.cc
new file mode 100644
index 0000000..66899f7
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_policy.cc
@@ -0,0 +1,184 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/memory/zram_writeback_policy.h"
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chromeos/ash/components/memory/memory.h"
+#include "chromeos/ash/components/memory/swap_configuration.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash::memory {
+
+namespace {
+
+// We are not considering anything other than 4K pages at the moment.
+constexpr uint64_t kPagesize = 4096;
+constexpr int kMBtoBytesShift = 20;
+
+class ZramWritebackPolicyImpl : public ZramWritebackPolicy {
+ public:
+  ZramWritebackPolicyImpl() { SetParams(ZramWritebackParams::Get()); }
+  ~ZramWritebackPolicyImpl() override = default;
+
+  base::TimeDelta GetWritebackTimerInterval() override {
+    return params_.periodic_time;
+  }
+
+  void Initialize(uint64_t zram_disk_size_mb,
+                  uint64_t writeback_size_mb) override {
+    writeback_device_size_pages_ =
+        (writeback_size_mb << kMBtoBytesShift) / kPagesize;
+    zram_size_pages_ = (zram_disk_size_mb << kMBtoBytesShift) / kPagesize;
+  }
+
+  base::TimeDelta GetCurrentWritebackIdleTime() override {
+    if (!CanWritebackIdle()) {
+      return base::TimeDelta::Max();
+    }
+
+    if (!UpdateMemoryInfo()) {
+      return base::TimeDelta::Max();
+    }
+
+    // Stay between idle_(min|max)_time.
+    uint64_t min_sec = params_.idle_min_time.InSeconds();
+    uint64_t max_sec = params_.idle_max_time.InSeconds();
+    double mem_utilization =
+        (1.0 -
+         (static_cast<double>(memory_info_.available) / memory_info_.total));
+
+    // Exponentially decay the writeback age vs. memory utilization. The reason
+    // we choose exponential decay is because we want to do as little work as
+    // possible when the system is under very low memory pressure. As pressure
+    // increases we want to start aggressively shrinking our idle age to force
+    // newer pages to be written back.
+    constexpr double kLambda = 5;
+    uint64_t age_sec =
+        (max_sec - min_sec) * pow(M_E, -kLambda * mem_utilization) + min_sec;
+
+    return base::Seconds(age_sec);
+  }
+
+  bool CanWritebackHugeIdle() override { return params_.writeback_huge_idle; }
+  bool CanWritebackIdle() override { return params_.writeback_idle; }
+  bool CanWritebackHuge() override { return params_.writeback_huge; }
+
+  uint64_t GetAllowedWritebackLimit() override {
+    // We haven't completed initialization.
+    if (writeback_device_size_pages_ == 0) {
+      LOG(WARNING) << "GetAllowedWritebackLimit called before Initialize";
+      NOTREACHED();
+      return 0;
+    }
+
+    // Basic sanity check on our configuration.
+    if (!CanWritebackIdle() && !CanWritebackHuge() && !CanWritebackHugeIdle()) {
+      return 0;
+    }
+
+    // Did we writeback too recently?
+    if (last_writeback_ != base::Time::Min()) {
+      const auto time_since_writeback = base::Time::Now() - last_writeback_;
+      if (time_since_writeback < params_.backoff_time) {
+        return 0;
+      }
+    }
+
+    // We need to decide how many pages we will want to write back total, this
+    // includes huge and idle if they are both enabled. The calculation is based
+    // on zram utilization, writeback utilization, and memory pressure.
+    uint64_t num_pages = 0;
+
+    // Update metrics so we can make a decision.
+    if (!UpdateZramMetrics()) {
+      return 0;
+    }
+
+    // All calculations are performed in basis points, 100 bps = 1.00%. The
+    // number of pages allowed to be written back follows a simple linear
+    // relationship. The allowable range is [min_pages, max_pages], and the
+    // writeback limit will be the (zram utilization) * the range, that is, the
+    // more zram we're using the more we're going to allow to be written back.
+    constexpr uint32_t kBps = 100 * 100;
+    uint64_t pages_currently_written_back = zram_bd_stats_.bd_count;
+    uint32_t zram_utilization_bps =
+        ((zram_mm_stats_.orig_data_size / kPagesize) * kBps) / zram_size_pages_;
+    num_pages = zram_utilization_bps * params_.max_pages / kBps;
+
+    // And try to limit it to the approximate number of free backing device
+    // pages (if it's less).
+    uint64_t free_bd_pages =
+        writeback_device_size_pages_ - pages_currently_written_back;
+    num_pages = std::min(num_pages, free_bd_pages);
+
+    // Finally enforce the limits, we won't even attempt writeback if we
+    // cannot writeback at least the min, and we will cap to the max if it's
+    // greater.
+    num_pages = std::min(num_pages, params_.max_pages);
+    if (num_pages < params_.min_pages) {
+      // Configured to not writeback fewer than kCrOSWritebackMinPages.
+      return 0;
+    }
+
+    last_writeback_ = base::Time::Now();
+    return num_pages;
+  }
+
+ protected:
+  virtual bool UpdateZramMetrics() {
+    // Refresh our current metrics.
+    if (!GetZramMmStats(&zram_mm_stats_)) {
+      LOG(ERROR) << "Unable to get zram mm stats";
+      return false;
+    }
+
+    if (!GetZramBdStats(&zram_bd_stats_)) {
+      LOG(ERROR) << "Unable to get zram bd stats";
+      return false;
+    }
+
+    return true;
+  }
+
+  virtual bool UpdateMemoryInfo() {
+    return base::GetSystemMemoryInfo(&memory_info_);
+  }
+
+  virtual void SetParams(const ZramWritebackParams& params) {
+    params_ = params;
+  }
+
+  ZramMmStat zram_mm_stats_;
+  ZramBdStat zram_bd_stats_;
+  base::SystemMemoryInfoKB memory_info_;
+
+  ZramWritebackParams params_;
+
+  uint64_t writeback_device_size_pages_ = 0;
+  uint64_t zram_size_pages_ = 0;
+
+  base::Time last_writeback_{base::Time::Min()};
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<ZramWritebackPolicy> COMPONENT_EXPORT(ASH_MEMORY)
+    ZramWritebackPolicy::Get() {
+  return std::make_unique<ZramWritebackPolicyImpl>();
+}
+
+}  // namespace ash::memory
diff --git a/chromeos/ash/components/memory/zram_writeback_policy.h b/chromeos/ash/components/memory/zram_writeback_policy.h
new file mode 100644
index 0000000..f7d299b
--- /dev/null
+++ b/chromeos/ash/components/memory/zram_writeback_policy.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_POLICY_H_
+#define CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_POLICY_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+
+namespace ash::memory {
+
+// The policy is responsible for when things happen, and how much, the backend
+// does the actual work.
+class COMPONENT_EXPORT(ASH_MEMORY) ZramWritebackPolicy {
+ public:
+  virtual ~ZramWritebackPolicy() = default;
+
+  static std::unique_ptr<ZramWritebackPolicy> Get();
+
+  virtual void Initialize(uint64_t zram_disk_size_mb,
+                          uint64_t writeback_size_mb) = 0;
+
+  virtual bool CanWritebackHugeIdle() = 0;
+  virtual bool CanWritebackIdle() = 0;
+  virtual bool CanWritebackHuge() = 0;
+
+  // GetCurrentWritebackIdleTime will return the age the controller should pass
+  // to the backend for idle pages, this idle time applies to both Idle and Huge
+  // Idle writeback modes.
+  virtual base::TimeDelta GetCurrentWritebackIdleTime() = 0;
+
+  // GetAllowedWritebackLimit returns the number of pages the backend should
+  // writeback (this interval).
+  virtual uint64_t GetAllowedWritebackLimit() = 0;
+
+  // GetWritebackTimerInterval is the period in which the controller should
+  // query the policy.
+  virtual base::TimeDelta GetWritebackTimerInterval() = 0;
+};
+
+}  // namespace ash::memory
+
+#endif  // CHROMEOS_ASH_COMPONENTS_MEMORY_ZRAM_WRITEBACK_POLICY_H_
diff --git a/chromeos/ash/services/auth_factor_config/BUILD.gn b/chromeos/ash/services/auth_factor_config/BUILD.gn
new file mode 100644
index 0000000..d99721a
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash, "Only ash builds can depend on //chromeos/ash")
+
+static_library("auth_factor_config") {
+  sources = [
+    "auth_factor_config.cc",
+    "auth_factor_config.h",
+    "in_process_instances.cc",
+    "in_process_instances.h",
+    "recovery_factor_editor.cc",
+    "recovery_factor_editor.h",
+  ]
+
+  deps = [ "//ash/constants" ]
+
+  public_deps = [
+    "//chromeos/ash/services/auth_factor_config/public/mojom",
+    "//mojo/public/cpp/bindings",
+  ]
+}
diff --git a/chromeos/ash/services/auth_factor_config/OWNERS b/chromeos/ash/services/auth_factor_config/OWNERS
new file mode 100644
index 0000000..3aa4c12
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/OWNERS
@@ -0,0 +1,3 @@
+antrim@chromium.org
+
+file://ash/login/LOGIN_LOCK_OWNERS
diff --git a/chromeos/ash/services/auth_factor_config/auth_factor_config.cc b/chromeos/ash/services/auth_factor_config/auth_factor_config.cc
new file mode 100644
index 0000000..13c2c6c
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/auth_factor_config.cc
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/services/auth_factor_config/auth_factor_config.h"
+
+#include "ash/constants/ash_features.h"
+
+namespace ash::auth {
+
+AuthFactorConfig::AuthFactorConfig() = default;
+
+AuthFactorConfig::~AuthFactorConfig() = default;
+
+void AuthFactorConfig::BindReceiver(
+    mojo::PendingReceiver<mojom::AuthFactorConfig> receiver) {
+  receivers_.Add(this, std::move(receiver));
+}
+
+void AuthFactorConfig::ObserveFactorChanges(
+    mojo::PendingRemote<mojom::FactorObserver> observer) {
+  observers_.Add(std::move(observer));
+}
+
+void AuthFactorConfig::NotifyFactorObservers(mojom::AuthFactor changed_factor) {
+  for (auto& observer : observers_)
+    observer->OnFactorChanged(changed_factor);
+}
+
+void AuthFactorConfig::IsSupported(const std::string& auth_token,
+                                   mojom::AuthFactor factor,
+                                   base::OnceCallback<void(bool)> callback) {
+  VLOG(1) << "AuthFactorConfig::IsSupported is a fake";
+  std::move(callback).Run(features::IsCryptohomeRecoverySetupEnabled());
+}
+
+void AuthFactorConfig::IsConfigured(const std::string& auth_token,
+                                    mojom::AuthFactor factor,
+                                    base::OnceCallback<void(bool)> callback) {
+  if (!features::IsCryptohomeRecoverySetupEnabled()) {
+    // Log always, crash on debug builds.
+    LOG(ERROR) << "AuthFactorConfig::IsConfigured is a fake";
+    NOTIMPLEMENTED();
+    std::move(callback).Run(false);
+    return;
+  }
+
+  std::move(callback).Run(recovery_configured_);
+}
+
+void AuthFactorConfig::GetManagementType(
+    const std::string& auth_token,
+    mojom::AuthFactor factor,
+    base::OnceCallback<void(mojom::ManagementType)> callback) {
+  if (!features::IsCryptohomeRecoverySetupEnabled()) {
+    // Log always, crash on debug builds.
+    LOG(ERROR) << "AuthFactorConfig::GetManagementType is a fake";
+    NOTIMPLEMENTED();
+    std::move(callback).Run(mojom::ManagementType::kNone);
+    return;
+  }
+
+  std::move(callback).Run(mojom::ManagementType::kNone);
+}
+
+void AuthFactorConfig::IsEditable(const std::string& auth_token,
+                                  mojom::AuthFactor factor,
+                                  base::OnceCallback<void(bool)> callback) {
+  if (!features::IsCryptohomeRecoverySetupEnabled()) {
+    // Log always, crash on debug builds.
+    LOG(ERROR) << "AuthFactorConfig::IsEditable is a fake";
+    NOTIMPLEMENTED();
+    std::move(callback).Run(false);
+    return;
+  }
+
+  std::move(callback).Run(true);
+}
+
+}  // namespace ash::auth
diff --git a/chromeos/ash/services/auth_factor_config/auth_factor_config.h b/chromeos/ash/services/auth_factor_config/auth_factor_config.h
new file mode 100644
index 0000000..d21b32d
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/auth_factor_config.h
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_AUTH_FACTOR_CONFIG_H_
+#define CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_AUTH_FACTOR_CONFIG_H_
+
+#include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
+
+namespace ash::auth {
+
+// The implementation of the AuthFactorConfig service.
+// TODO(crbug.com/1327627): This will eventually communicate with cryptohome,
+// but it is currently a fake and only maintains a boolean corresponding to the
+// current state. No changes are sent to cryptohome. The fake reports that
+// cryptohome recovery is supported only if the CryptohomeRecoverySetup feature
+// flag is enabled.
+class AuthFactorConfig : public mojom::AuthFactorConfig {
+ public:
+  AuthFactorConfig();
+  ~AuthFactorConfig() override;
+
+  AuthFactorConfig(const AuthFactorConfig&) = delete;
+  AuthFactorConfig& operator=(const AuthFactorConfig&) = delete;
+
+  void BindReceiver(mojo::PendingReceiver<mojom::AuthFactorConfig> receiver);
+
+  void ObserveFactorChanges(
+      mojo::PendingRemote<mojom::FactorObserver>) override;
+  void IsSupported(const std::string& auth_token,
+                   mojom::AuthFactor factor,
+                   base::OnceCallback<void(bool)>) override;
+  void IsConfigured(const std::string& auth_token,
+                    mojom::AuthFactor factor,
+                    base::OnceCallback<void(bool)>) override;
+  void GetManagementType(
+      const std::string& auth_token,
+      mojom::AuthFactor factor,
+      base::OnceCallback<void(mojom::ManagementType)>) override;
+  void IsEditable(const std::string& auth_token,
+                  mojom::AuthFactor factor,
+                  base::OnceCallback<void(bool)>) override;
+
+ private:
+  friend class RecoveryFactorEditor;
+
+  void NotifyFactorObservers(mojom::AuthFactor changed_factor);
+
+  bool recovery_configured_ = false;
+
+  mojo::ReceiverSet<mojom::AuthFactorConfig> receivers_;
+  mojo::RemoteSet<mojom::FactorObserver> observers_;
+};
+
+}  // namespace ash::auth
+
+#endif  // CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_AUTH_FACTOR_CONFIG_H_
diff --git a/chromeos/ash/services/auth_factor_config/in_process_instances.cc b/chromeos/ash/services/auth_factor_config/in_process_instances.cc
new file mode 100644
index 0000000..8e408a2
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/in_process_instances.cc
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/services/auth_factor_config/in_process_instances.h"
+#include <utility>
+#include "base/no_destructor.h"
+#include "chromeos/ash/services/auth_factor_config/auth_factor_config.h"
+#include "chromeos/ash/services/auth_factor_config/recovery_factor_editor.h"
+
+namespace ash::auth {
+
+namespace {
+
+// TODO(crbug.com/1327627): We probably want to initialize these later: Not on
+// chrome startup but when a user logs in. Optionally we could delay
+// initialization to first use (static member of a getter) or even create new
+// instances of the services for each webui that consumes them.
+base::NoDestructor<AuthFactorConfig> auth_factor_config;
+base::NoDestructor<RecoveryFactorEditor> recovery_factor_editor(
+    auth_factor_config.get());
+
+}  // namespace
+
+void BindToAuthFactorConfig(
+    mojo::PendingReceiver<mojom::AuthFactorConfig> receiver) {
+  auth_factor_config->BindReceiver(std::move(receiver));
+}
+
+void BindToRecoveryFactorEditor(
+    mojo::PendingReceiver<mojom::RecoveryFactorEditor> receiver) {
+  recovery_factor_editor->BindReceiver(std::move(receiver));
+}
+
+}  // namespace ash::auth
diff --git a/chromeos/ash/services/auth_factor_config/in_process_instances.h b/chromeos/ash/services/auth_factor_config/in_process_instances.h
new file mode 100644
index 0000000..ec979f1
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/in_process_instances.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_IN_PROCESS_INSTANCES_H_
+#define CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_IN_PROCESS_INSTANCES_H_
+
+#include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom-forward.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+
+// This file contains functions to bind mojo clients for the auth factor config
+// related services to server implementations. The server implementations are
+// singletons defined in the .cc file.
+
+namespace ash::auth {
+
+void BindToAuthFactorConfig(
+    mojo::PendingReceiver<mojom::AuthFactorConfig> receiver);
+
+void BindToRecoveryFactorEditor(
+    mojo::PendingReceiver<mojom::RecoveryFactorEditor> receiver);
+
+}  // namespace ash::auth
+
+#endif  // CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_IN_PROCESS_INSTANCES_H_
diff --git a/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc b/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc
new file mode 100644
index 0000000..9a3394f
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/services/auth_factor_config/recovery_factor_editor.h"
+#include "ash/constants/ash_features.h"
+#include "chromeos/ash/services/auth_factor_config/auth_factor_config.h"
+
+namespace ash::auth {
+
+RecoveryFactorEditor::RecoveryFactorEditor(AuthFactorConfig* auth_factor_config)
+    : auth_factor_config_(auth_factor_config) {}
+RecoveryFactorEditor::~RecoveryFactorEditor() = default;
+
+void RecoveryFactorEditor::BindReceiver(
+    mojo::PendingReceiver<mojom::RecoveryFactorEditor> receiver) {
+  receivers_.Add(this, std::move(receiver));
+}
+
+void RecoveryFactorEditor::Configure(
+    const std::string& auth_token,
+    bool enabled,
+    base::OnceCallback<void(ConfigureResult)> callback) {
+  if (!features::IsCryptohomeRecoverySetupEnabled()) {
+    // Log always, crash on debug builds.
+    LOG(ERROR) << "AuthFactorConfig::Configure is a fake";
+    NOTIMPLEMENTED();
+    std::move(callback).Run(ConfigureResult::kClientError);
+    return;
+  }
+
+  auth_factor_config_->recovery_configured_ = enabled;
+  std::move(callback).Run(ConfigureResult::kSuccess);
+  auth_factor_config_->NotifyFactorObservers(mojom::AuthFactor::kRecovery);
+}
+
+}  // namespace ash::auth
diff --git a/chromeos/ash/services/auth_factor_config/recovery_factor_editor.h b/chromeos/ash/services/auth_factor_config/recovery_factor_editor.h
new file mode 100644
index 0000000..9a3152c
--- /dev/null
+++ b/chromeos/ash/services/auth_factor_config/recovery_factor_editor.h
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_RECOVERY_FACTOR_EDITOR_H_
+#define CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_RECOVERY_FACTOR_EDITOR_H_
+
+#include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace ash::auth {
+
+class AuthFactorConfig;
+
+// The implementation of the RecoveryFactorEditor service.
+// TODO(crbug.com/1327627): This is currently a fake and only flips a boolean
+// corresponding to the current state. No changes are sent to cryptohome.
+// Clients may use this API only if the CryptohomeRecoverySetup feature flag is
+// enabled.
+class RecoveryFactorEditor : public mojom::RecoveryFactorEditor {
+ public:
+  explicit RecoveryFactorEditor(AuthFactorConfig*);
+  ~RecoveryFactorEditor() override;
+
+  RecoveryFactorEditor(const RecoveryFactorEditor&) = delete;
+  RecoveryFactorEditor& operator=(const RecoveryFactorEditor&) = delete;
+
+  void BindReceiver(
+      mojo::PendingReceiver<mojom::RecoveryFactorEditor> receiver);
+
+  void Configure(const std::string& auth_token,
+                 bool enabled,
+                 base::OnceCallback<void(ConfigureResult)>) override;
+
+ private:
+  raw_ptr<AuthFactorConfig> auth_factor_config_;
+  mojo::ReceiverSet<mojom::RecoveryFactorEditor> receivers_;
+};
+
+}  // namespace ash::auth
+
+#endif  // CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_RECOVERY_FACTOR_EDITOR_H_
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index f9d8e73..a08a4ca9 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -64,6 +64,7 @@
     "policy_service.mojom",
     "power.mojom",
     "prefs.mojom",
+    "printing_metrics.mojom",
     "remoting.mojom",
     "resource_manager.mojom",
     "screen_manager.mojom",
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 06c8277..2f43385 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -57,6 +57,7 @@
 import "chromeos/crosapi/mojom/power.mojom";
 import "chromeos/crosapi/mojom/network_settings_service.mojom";
 import "chromeos/crosapi/mojom/prefs.mojom";
+import "chromeos/crosapi/mojom/printing_metrics.mojom";
 import "chromeos/crosapi/mojom/remoting.mojom";
 import "chromeos/crosapi/mojom/resource_manager.mojom";
 import "chromeos/crosapi/mojom/screen_manager.mojom";
diff --git a/chromeos/crosapi/mojom/printing_metrics.mojom b/chromeos/crosapi/mojom/printing_metrics.mojom
new file mode 100644
index 0000000..feab17b
--- /dev/null
+++ b/chromeos/crosapi/mojom/printing_metrics.mojom
@@ -0,0 +1,66 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module crosapi.mojom;
+
+import "mojo/public/mojom/base/values.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
+
+
+// Listens to events dispatched by ash::PrintJobHistoryService in ash and
+// sends them to lacros. See chrome.printingMetrics.onPrintJobFinished event.
+// Next version: 1
+// Next method id: 1
+[Stable, Uuid="64680b44-074e-4aa0-93ce-3c0956e192a0"]
+interface PrintJobObserverForProfile {
+  // Forwards OnPrintJobFinished() event from ash::PrintJobHistoryService
+  // to the browser.
+  //
+  // The supplied value is the chrome.printintMetrics.PrintJobInfo object
+  // serialized as base::Value.
+  // See chrome/common/extensions/api/printing_metrics.idl for details.
+  //
+  // We use mojo_base.mojom.Value as the type because:
+  // * The idl for the API is stable and can easily be serialized and
+  //   deserialized into base::Value
+  // * Both the supplier and consumer of this information uses a type
+  //   interchangeable with mojo_base.mojom.Value. While we could add
+  //   a translation layer to a strongly typed mojom struct, this adds an
+  //   overhead and the potential for errors with no benefit.
+  OnPrintJobFinished@0(mojo_base.mojom.Value print_job);
+};
+
+// PrintingMetricsForProfile processes chrome.printingMetrics API calls
+// for the associated profile on the ash side.
+// Next version: 1
+// Next method id: 1
+[Stable, Uuid="27ba5fb3-5624-4b6d-8909-8243a0ffd7aa"]
+interface PrintingMetricsForProfile {
+  // Implements chrome.printingMetrics.getPrintJobs(...)
+  //
+  // The return values are chrome.printingMetrics.PrintJobInfo objects
+  // serialized as base::Value.
+  // See chrome/common/extensions/api/printing_metrics.idl for details.
+  //
+  // We use mojo_base.mojom.Value as the type because:
+  // * The idl for the API is stable and can easily be serialized and
+  //   deserialized into base::Value
+  // * Both the supplier and consumer of this information uses a type
+  //   interchangeable with mojo_base.mojom.Value. While we could add
+  //   a translation layer to a strongly typed mojom struct, this adds an
+  //   overhead and the potential for errors with no benefit.
+  GetPrintJobs@0() => (array<mojo_base.mojom.Value> print_jobs);
+};
+
+// PrintingMetrics is the entry point for chrome.printingMetrics API
+// on the ash side.
+// Next version: 1
+// Next method id: 1
+[Stable, Uuid="e27e7b78-f2ce-4d06-8bc9-e247f8edf355"]
+interface PrintingMetrics {
+  // Registers a service for the main profile and allows ash to
+  // send printing events via |observer|.
+  RegisterForMainProfile@0(pending_receiver<PrintingMetricsForProfile> receiver,
+    pending_remote<PrintJobObserverForProfile> observer);
+};
\ No newline at end of file
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.cc b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
index 2acdc6d..156c7c2 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
@@ -634,6 +634,57 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void SwapZramEnableWriteback(
+      uint32_t size_mb,
+      DBusMethodCallback<std::string> callback) override {
+    dbus::MethodCall method_call(debugd::kDebugdInterface,
+                                 debugd::kSwapZramEnableWriteback);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(size_mb);
+    debugdaemon_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&DebugDaemonClientImpl::OnZramWritebackOptionResult,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void SwapZramSetWritebackLimit(
+      uint32_t limit_pages,
+      DBusMethodCallback<std::string> callback) override {
+    dbus::MethodCall method_call(debugd::kDebugdInterface,
+                                 debugd::kSwapZramSetWritebackLimit);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(limit_pages);
+    debugdaemon_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&DebugDaemonClientImpl::OnZramWritebackOptionResult,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void SwapZramMarkIdle(uint32_t age_seconds,
+                        DBusMethodCallback<std::string> callback) override {
+    dbus::MethodCall method_call(debugd::kDebugdInterface,
+                                 debugd::kSwapZramMarkIdle);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(age_seconds);
+    debugdaemon_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&DebugDaemonClientImpl::OnZramWritebackOptionResult,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void InitiateSwapZramWriteback(
+      debugd::ZramWritebackMode mode,
+      DBusMethodCallback<std::string> callback) override {
+    dbus::MethodCall method_call(debugd::kDebugdInterface,
+                                 "InitiateSwapZramWriteback");
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(mode);
+    debugdaemon_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&DebugDaemonClientImpl::OnZramWritebackOptionResult,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void StopPacketCapture(const std::string& handle) override {
     dbus::MethodCall method_call(debugd::kDebugdInterface,
                                  debugd::kPacketCaptureStop);
@@ -1013,6 +1064,24 @@
     std::move(callback).Run(std::move(flags));
   }
 
+  void OnZramWritebackOptionResult(DBusMethodCallback<std::string> callback,
+                                   dbus::Response* response) {
+    if (!response) {
+      std::move(callback).Run(absl::nullopt);
+      return;
+    }
+
+    std::string res;
+    dbus::MessageReader reader(response);
+    if (!reader.PopString(&res)) {
+      LOG(ERROR) << "Received a non-string response from dbus";
+      std::move(callback).Run(absl::nullopt);
+      return;
+    }
+
+    std::move(callback).Run(std::move(res));
+  }
+
   // Called when a D-Bus signal is initially connected.
   void SignalConnected(const std::string& interface_name,
                        const std::string& signal_name,
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.h b/chromeos/dbus/debug_daemon/debug_daemon_client.h
index cef275f4..4d5014a 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.h
@@ -300,6 +300,22 @@
                                 int32_t value,
                                 DBusMethodCallback<std::string> callback) = 0;
 
+  // Zram Writeback Dbus Messages
+  virtual void SwapZramEnableWriteback(
+      uint32_t size_mb,
+      DBusMethodCallback<std::string> callback) = 0;
+
+  virtual void SwapZramSetWritebackLimit(
+      uint32_t limit_pages,
+      DBusMethodCallback<std::string> callback) = 0;
+
+  virtual void SwapZramMarkIdle(uint32_t age_seconds,
+                                DBusMethodCallback<std::string> callback) = 0;
+
+  virtual void InitiateSwapZramWriteback(
+      debugd::ZramWritebackMode mode,
+      DBusMethodCallback<std::string> callback) = 0;
+
   // Stops the packet capture process identified with |handle|. |handle| is a
   // unique process identifier that is returned from debugd's PacketCaptureStart
   // D-Bus method when the packet capture process is started. Stops all on-going
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
index b850b394..5df9f31 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
@@ -64,6 +64,30 @@
   std::move(callback).Run(std::string());
 }
 
+void FakeDebugDaemonClient::SwapZramEnableWriteback(
+    uint32_t size_mb,
+    DBusMethodCallback<std::string> callback) {
+  std::move(callback).Run(std::string());
+}
+
+void FakeDebugDaemonClient::SwapZramSetWritebackLimit(
+    uint32_t limit_pages,
+    DBusMethodCallback<std::string> callback) {
+  std::move(callback).Run(std::string());
+}
+
+void FakeDebugDaemonClient::SwapZramMarkIdle(
+    uint32_t age_seconds,
+    DBusMethodCallback<std::string> callback) {
+  std::move(callback).Run(std::string());
+}
+
+void FakeDebugDaemonClient::InitiateSwapZramWriteback(
+    debugd::ZramWritebackMode mode,
+    DBusMethodCallback<std::string> callback) {
+  std::move(callback).Run(std::string());
+}
+
 std::string FakeDebugDaemonClient::GetTracingAgentName() {
   return kCrOSTracingAgentName;
 }
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
index ac1c5bbb..f72b102 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
@@ -43,6 +43,20 @@
   void SetSwapParameter(const std::string& parameter,
                         int32_t value,
                         DBusMethodCallback<std::string> callback) override;
+  void SwapZramEnableWriteback(
+      uint32_t size_mb,
+      DBusMethodCallback<std::string> callback) override;
+
+  void SwapZramSetWritebackLimit(
+      uint32_t limit_pages,
+      DBusMethodCallback<std::string> callback) override;
+
+  void SwapZramMarkIdle(uint32_t age_seconds,
+                        DBusMethodCallback<std::string> callback) override;
+
+  void InitiateSwapZramWriteback(
+      debugd::ZramWritebackMode mode,
+      DBusMethodCallback<std::string> callback) override;
   void StartAgentTracing(const base::trace_event::TraceConfig& trace_config,
                          StartAgentTracingCallback callback) override;
   void StopAgentTracing(StopAgentTracingCallback callback) override;
diff --git a/chromeos/strings/chromeos_strings_da.xtb b/chromeos/strings/chromeos_strings_da.xtb
index 7a2d329e..23e63a5 100644
--- a/chromeos/strings/chromeos_strings_da.xtb
+++ b/chromeos/strings/chromeos_strings_da.xtb
@@ -153,6 +153,7 @@
 <translation id="2163937499206714165">Aktivér mørkt tilstand</translation>
 <translation id="2177831236093724629">Der blev fundet <ph name="LINK_BEGIN" />ukvalificerede komponenter<ph name="LINK_END" />. Opdater til den nyeste version af Chrome OS for at sikre, at alle kvalificerede komponenter identificeres.</translation>
 <translation id="2180197493692062006">Der opstod en fejl. Prøv at åbne appen igen.</translation>
+<translation id="2182088270354640012">Tak for din feedback. Din feedback er med til at forbedre Chrome OS og gennemgås af Chrome OS-teamet. Vi modtager et stort antal rapporter, så du modtager ikke et direkte svar.</translation>
 <translation id="2209788852729124853">Nulstil trafiktællere</translation>
 <translation id="2212733584906323460">Navneløsning</translation>
 <translation id="2217935453350629363">Nuværende hastighed</translation>
@@ -201,6 +202,7 @@
 <translation id="253029298928638905">Genstarter...</translation>
 <translation id="2533048460510040082">Foreslået indhold i Hjælp</translation>
 <translation id="2536159006530886390">Der kan ikke oprettes forbindelse til internettet.</translation>
+<translation id="256360469103926202">Din feedback er med til at forbedre Chrome OS og gennemgås af vores team. Vi modtager et stort antal rapporter, så vi kan ikke sende dig et svar.</translation>
 <translation id="2570743873672969996">Kører testen <ph name="TEST_NAME" />...</translation>
 <translation id="2584559707064218956">Gå til Indstillinger for at konfigurere</translation>
 <translation id="2619761439309613843">Nyt hver dag</translation>
@@ -256,6 +258,7 @@
 <translation id="3226405216343213872">Søger efter scannere</translation>
 <translation id="3246869037381808805">Udskriftjobs, der er ældre end 1 dag, fjernes</translation>
 <translation id="3268178239013324452">Handlingen mislykkedes – Åben låge</translation>
+<translation id="3275729367986477355">Billede til avatar</translation>
 <translation id="3283504360622356314">{0,plural, =1{Rediger fil}one{Rediger fil}other{Rediger filer}}</translation>
 <translation id="3286515922899063534"><ph name="CURRENT" /> GHz</translation>
 <translation id="3291996639387199448">Nøgleretning</translation>
@@ -429,6 +432,7 @@
 <translation id="4793710386569335688">Få yderligere hjælp ved at gå til <ph name="BEGIN_LINK" />Hjælp<ph name="END_LINK" />.</translation>
 <translation id="4793756956024303490">Algoritme til komprimering</translation>
 <translation id="4794140124556169553">Systemets ydeevne kan blive påvirket under kørsel af en CPU-test</translation>
+<translation id="4800589996161293643">Hjælpeforum for Chromebook</translation>
 <translation id="4804818685124855865">Afbryd</translation>
 <translation id="4808449224298348341">Udskriftsjobbet <ph name="DOCUMENT_TITLE" /> blev annulleret</translation>
 <translation id="4809927044794281115">Lyst tema</translation>
@@ -487,6 +491,7 @@
 <translation id="5212543919916444558">Jeg kan ikke finde noget på skærmen, jeg kan hjælpe med. Prøv at trykke på mikrofonen for at spørge mig om noget.</translation>
 <translation id="5212593641110061691">Tabloid</translation>
 <translation id="5222676887888702881">Log ud</translation>
+<translation id="522307662484862935">Undlad at medtage mailadresse</translation>
 <translation id="5227902338748591677">Tidsplan for Mørkt tema</translation>
 <translation id="5234764350956374838">Luk</translation>
 <translation id="5252456968953390977">Roaming</translation>
@@ -613,6 +618,7 @@
 <translation id="6284632978374966585">Aktivér Mørkt tema</translation>
 <translation id="6295178529664209245">Vil du fortsætte reparationen?</translation>
 <translation id="6302401976930124515"><ph name="TEST_NAME" />-testen er annulleret</translation>
+<translation id="631063167932043783">Appen Udforsk</translation>
 <translation id="6319207335391420837">Opdater firmware på <ph name="DEVICE_NAME" /></translation>
 <translation id="6321407676395378991">Aktivér Pauseskærm</translation>
 <translation id="6325525973963619867">Mislykket</translation>
@@ -719,6 +725,7 @@
 <translation id="7297226631177386107">Der kan ikke oprettes forbindelse gennem firewallen til HTTPS-websites</translation>
 <translation id="7302860742311162920">ICCID</translation>
 <translation id="7305884605064981971">EDGE</translation>
+<translation id="7308203371573257315">Spørg eksperterne i hjælpeforummet for Chromebook</translation>
 <translation id="7319430975418800333">A3</translation>
 <translation id="7343581795491695942"><ph name="QUERY_TEXT" />; <ph name="RESULT_TEXT" />; Tryk på søgetasten og mellemrumstasten for at se resultater i Google Søgning.</translation>
 <translation id="7343649194310845056">Netværksenheder</translation>
@@ -761,6 +768,7 @@
 <translation id="773153675489693198">Antal gennemførte cyklusser</translation>
 <translation id="7747039790905080783">Forhåndsdelt nøgle</translation>
 <translation id="7762130827864645708">Din adgangskode er ændret. Brug den nye adgangskode fremover.</translation>
+<translation id="7762839321248874531">Send <ph name="BEGIN_LINK1" />system- og appoplysninger<ph name="END_LINK1" /> samt <ph name="BEGIN_LINK2" />metrics<ph name="END_LINK2" /></translation>
 <translation id="7763470514545477072">Match af domænesuffiks</translation>
 <translation id="7769672763586021400">Model-id</translation>
 <translation id="7784116172884276937">Der er ikke konfigureret nogen DNS-servere</translation>
@@ -830,6 +838,7 @@
 <translation id="8382302752385457774">White-label</translation>
 <translation id="8395584934117017006">Denne <ph name="DEVICE_TYPE" /> administreres af en virksomhed</translation>
 <translation id="8398927464629426868">Den aktuelle hastighed for opladning eller afladning på enheden</translation>
+<translation id="8420955526972171689">Kør tests og fejlfinding af hardwareproblemer</translation>
 <translation id="8422748173858722634">IMEI</translation>
 <translation id="8428073762635678092">Indbygget tastatur</translation>
 <translation id="8431300646573772016">Nyt i Chrome OS</translation>
diff --git a/chromeos/strings/chromeos_strings_mn.xtb b/chromeos/strings/chromeos_strings_mn.xtb
index 6e7823e8..f19ee4f 100644
--- a/chromeos/strings/chromeos_strings_mn.xtb
+++ b/chromeos/strings/chromeos_strings_mn.xtb
@@ -153,6 +153,7 @@
 <translation id="2163937499206714165">Бараан горимыг асаах</translation>
 <translation id="2177831236093724629"><ph name="LINK_BEGIN" />Шаардлага хангаагүй бүрэлдэхүүн хэсгүүд<ph name="LINK_END" /> олдлоо. Шаардлага хангасан бүх бүрэлдэхүүн хэсгийг таньсан эсэхийг шалгахын тулд Chrome OS-оо хамгийн сүүлийн үеийн хувилбар луу шинэчилнэ үү.</translation>
 <translation id="2180197493692062006">Алдаа гарлаа. Аппыг дахин нээж үзнэ үү.</translation>
+<translation id="2182088270354640012">Санал хүсэлт ирүүлсэнд баярлалаа. Таны санал хүсэлт ChromeOS-г сайжруулахад туслах бөгөөд манай Chrome OS баг үнэлэх болно. Илгээсэн мэдээллийн тооноос болж та шууд хариу хүлээж авахгүй.</translation>
 <translation id="2209788852729124853">Ачаалал тоологчийг шинэчлэх</translation>
 <translation id="2212733584906323460">Нэр тодорхойлох</translation>
 <translation id="2217935453350629363">Одоогийн хурд</translation>
@@ -201,6 +202,7 @@
 <translation id="253029298928638905">Дахин эхлүүлж байна...</translation>
 <translation id="2533048460510040082">Санал болгосон тусламжийн контент</translation>
 <translation id="2536159006530886390">Интернэтэд холбогдох боломжгүй байна.</translation>
+<translation id="256360469103926202">Таны санал хүсэлт ChromeOS-г сайжруулахад туслах бөгөөд манай баг үнэлэх болно. Олон тооны мэдээлэл байгаа тул бид хариу илгээх боломжгүй.</translation>
 <translation id="2570743873672969996"><ph name="TEST_NAME" /> тестийг ажиллуулж байна...</translation>
 <translation id="2584559707064218956">Тохируулахын тулд Тохиргоо руу очно уу</translation>
 <translation id="2619761439309613843">Өдөр тутам сэргээх</translation>
@@ -256,6 +258,7 @@
 <translation id="3226405216343213872">Сканнерыг хайж байна</translation>
 <translation id="3246869037381808805">1 хоногоос дээш хугацаагаар хадгалсан хэвлэлийн ажлуудыг хасах болно</translation>
 <translation id="3268178239013324452">Амжилтгүй болсон - Таг нээлттэй</translation>
+<translation id="3275729367986477355">Аватар зураг</translation>
 <translation id="3283504360622356314">{0,plural, =1{Файлыг засах}other{Файлуудыг засах}}</translation>
 <translation id="3286515922899063534"><ph name="CURRENT" />Гц</translation>
 <translation id="3291996639387199448">Түлхүүрийн чиглэл</translation>
@@ -429,6 +432,7 @@
 <translation id="4793710386569335688">Илүү их тусламж авах бол <ph name="BEGIN_LINK" />Тусламжийн төв<ph name="END_LINK" /> рүү очно уу.</translation>
 <translation id="4793756956024303490">Шахалтын алгоритм</translation>
 <translation id="4794140124556169553">Төв процессорын нэгжийн туршилтыг ажиллуулах нь таны системийн гүйцэтгэлд нөлөөлж болзошгүй</translation>
+<translation id="4800589996161293643">Chromebook-н нийгэмлэг</translation>
 <translation id="4804818685124855865">Таслах</translation>
 <translation id="4808449224298348341"><ph name="DOCUMENT_TITLE" /> хэвлэх ажлыг цуцалсан</translation>
 <translation id="4809927044794281115">Цайвар загвар</translation>
@@ -488,6 +492,7 @@
 Асуух зүйл байвал микрофоныг товшиж үзнэ үү.</translation>
 <translation id="5212593641110061691">Tabloid</translation>
 <translation id="5222676887888702881">Гарах</translation>
+<translation id="522307662484862935">Имэйл хаягийг бүү оруулаарай</translation>
 <translation id="5227902338748591677">Бараан загварын хуваарь</translation>
 <translation id="5234764350956374838">Хэрэгсэхгүй болго</translation>
 <translation id="5252456968953390977">Роуминг</translation>
@@ -614,6 +619,7 @@
 <translation id="6284632978374966585">Бараан загварыг асаах</translation>
 <translation id="6295178529664209245">Засварыг үргэлжлүүлэх үү?</translation>
 <translation id="6302401976930124515"><ph name="TEST_NAME" /> тестийг цуцалсан байна</translation>
+<translation id="631063167932043783">Аппыг судлах</translation>
 <translation id="6319207335391420837">Жижиг программыг <ph name="DEVICE_NAME" /> дээр шинэчлэх</translation>
 <translation id="6321407676395378991">Дэлгэц амраагчийг асаах</translation>
 <translation id="6325525973963619867">Амжилтгүй боллоо</translation>
@@ -720,6 +726,7 @@
 <translation id="7297226631177386107">HTTPS вебсайтуудад галт хана дундуур холбогдох боломжгүй</translation>
 <translation id="7302860742311162920">ICCID</translation>
 <translation id="7305884605064981971">EDGE</translation>
+<translation id="7308203371573257315">Chromebook-н тусламжийн хэлэлцүүлгийн мэргэжилтнүүдээс асуугаарай</translation>
 <translation id="7319430975418800333">A3</translation>
 <translation id="7343581795491695942"><ph name="QUERY_TEXT" />; <ph name="RESULT_TEXT" />; Google Хайлтад илэрц харахын тулд Search болон Space-г дарна уу.</translation>
 <translation id="7343649194310845056">Сүлжээний төхөөрөмжүүд</translation>
@@ -762,6 +769,7 @@
 <translation id="773153675489693198">Мөчлөгийн тоо</translation>
 <translation id="7747039790905080783">Урьдчилан хуваалцсан түлхүүр</translation>
 <translation id="7762130827864645708">Таны нууц үгийг амжилттай өөрчиллөө. Одооноос шинэ нууц үгийг ашиглана уу.</translation>
+<translation id="7762839321248874531"><ph name="BEGIN_LINK1" />Систем, аппын мэдээлэл<ph name="END_LINK1" /> болон <ph name="BEGIN_LINK2" />хэмжигдэхүүн<ph name="END_LINK2" /> илгээх</translation>
 <translation id="7763470514545477072">Домэйн дагаврын тохирол</translation>
 <translation id="7769672763586021400">Загварын ID</translation>
 <translation id="7784116172884276937">Тохируулсан DNS сервер байхгүй</translation>
@@ -831,6 +839,7 @@
 <translation id="8382302752385457774">Цагаан-шошго</translation>
 <translation id="8395584934117017006">Энэ <ph name="DEVICE_TYPE" />-г байгууллага удирддаг</translation>
 <translation id="8398927464629426868">Төхөөрөмжийн одоогийн цэнэглэгдэж эсвэл цэнэгээ алдаж байгаа хувь</translation>
+<translation id="8420955526972171689">Техник хангамжийн асуудлуудыг шалгах, алдааг олж засварлаарай</translation>
 <translation id="8422748173858722634">IMEI</translation>
 <translation id="8428073762635678092">Дотоод гар</translation>
 <translation id="8431300646573772016">ChromeOS-н шинэ зүйлс</translation>
diff --git a/chromeos/strings/chromeos_strings_uz.xtb b/chromeos/strings/chromeos_strings_uz.xtb
index 2d0182c..1884cf7 100644
--- a/chromeos/strings/chromeos_strings_uz.xtb
+++ b/chromeos/strings/chromeos_strings_uz.xtb
@@ -153,6 +153,7 @@
 <translation id="2163937499206714165">Tungi rejimni yoqish</translation>
 <translation id="2177831236093724629"><ph name="LINK_BEGIN" />Mos boʻlmagan komponentlar<ph name="LINK_END" /> topildi. Barcha mos komponentlarni identifikatsiyalash uchun Chrome OS tizimini eng oxirgi versiyasiga yangilang.</translation>
 <translation id="2180197493692062006">Xatolik yuz berdi. Ilovani qayta ochishga urining.</translation>
+<translation id="2182088270354640012">Fikr-mulohazangiz uchun tashakkur! Fikr-mulohazangiz Chrome OS tizimini yaxshilashimizga yordam beradi va Chrome OS jamoasi tomonidan koʻrib chiqiladi. Murojaatlar soni juda koʻpligi uchun har biriga javob qaytara olmaymiz.</translation>
 <translation id="2209788852729124853">Traffik hisoblagichlarini tiklash</translation>
 <translation id="2212733584906323460">NS parametrlari</translation>
 <translation id="2217935453350629363">Joriy tezlik</translation>
@@ -201,6 +202,7 @@
 <translation id="253029298928638905">Oʻchib yonadi…</translation>
 <translation id="2533048460510040082">Tavsiya etilgan yordam kontenti</translation>
 <translation id="2536159006530886390">Internetga ulana olmadi.</translation>
+<translation id="256360469103926202">Fikr-mulohazangiz ChromeOSni yaxshilashimizga yordam beradi va jamoamiz tomonidan koʻrib chiqiladi. Murojaatlar soni juda koʻpligi uchun har biriga javob qaytara olmaymiz.</translation>
 <translation id="2570743873672969996"><ph name="TEST_NAME" /> testi bajarilmoqda...</translation>
 <translation id="2584559707064218956">Sozlash uchun Sozlamalarni oching</translation>
 <translation id="2619761439309613843">Kundalik yangilash</translation>
@@ -256,6 +258,7 @@
 <translation id="3226405216343213872">Skanerlar qidirilmoqda</translation>
 <translation id="3246869037381808805">1 kundan eski chop etish vazifalari avtomatik tozalanadi</translation>
 <translation id="3268178239013324452">Bajarilmadi - Eshikchasi ochiq</translation>
+<translation id="3275729367986477355">Avatar rasmi</translation>
 <translation id="3283504360622356314">{0,plural, =1{Faylni tahrirlash}other{Fayllarni tahrirlash}}</translation>
 <translation id="3286515922899063534"><ph name="CURRENT" /> GHz</translation>
 <translation id="3291996639387199448">Kalit yoʻnalishi</translation>
@@ -429,6 +432,7 @@
 <translation id="4793710386569335688">Yana yordam olish uchun <ph name="BEGIN_LINK" />Yordam markazi<ph name="END_LINK" /> sahifasini oching.</translation>
 <translation id="4793756956024303490">Arxivlash algoritmi</translation>
 <translation id="4794140124556169553">Protsessorni tekshirish (CPU test) tizim unumdorligiga taʼsir qilishi mumkin</translation>
+<translation id="4800589996161293643">Chromebook hamjamiyati</translation>
 <translation id="4804818685124855865">Uzilish</translation>
 <translation id="4808449224298348341">Chop etilishi bekor qilindi: <ph name="DOCUMENT_TITLE" /></translation>
 <translation id="4809927044794281115">Kunduzgi mavzu</translation>
@@ -488,6 +492,7 @@
 Mikrofon ustiga bosing va savolni ayting.</translation>
 <translation id="5212593641110061691">Tabloid</translation>
 <translation id="5222676887888702881">Tizimdan chiqish</translation>
+<translation id="522307662484862935">Email manzil biriktirilmasin</translation>
 <translation id="5227902338748591677">Tungi mavzu rejasi</translation>
 <translation id="5234764350956374838">Yopish</translation>
 <translation id="5252456968953390977">Rouming</translation>
@@ -614,6 +619,7 @@
 <translation id="6284632978374966585">Tungi mavzuni yoqish</translation>
 <translation id="6295178529664209245">Tuzatishda davom etasizmi?</translation>
 <translation id="6302401976930124515"><ph name="TEST_NAME" /> sinovi bekor qilindi</translation>
+<translation id="631063167932043783">Ilovani varaqlash</translation>
 <translation id="6319207335391420837"><ph name="DEVICE_NAME" /> mikrodasturini yangilang</translation>
 <translation id="6321407676395378991">Ekran lavhasini yoqish</translation>
 <translation id="6325525973963619867">Amalga oshmadi</translation>
@@ -720,6 +726,7 @@
 <translation id="7297226631177386107">HTTPS saytlarga himoya devori orqali ulanish imkonsiz</translation>
 <translation id="7302860742311162920">ICCID</translation>
 <translation id="7305884605064981971">EDGE</translation>
+<translation id="7308203371573257315">Chromebook yordam forumi ekspertlari yordamini olish</translation>
 <translation id="7319430975418800333">A3</translation>
 <translation id="7343581795491695942"><ph name="QUERY_TEXT" />; <ph name="RESULT_TEXT" />; Natijalarni Google Qidiruvida chiqarish uchun Qidiruv + Boʻshliq tugmalarini bosing.</translation>
 <translation id="7343649194310845056">Tarmoq qurilmalari</translation>
@@ -762,6 +769,7 @@
 <translation id="773153675489693198">Davrlar soni</translation>
 <translation id="7747039790905080783">Umumiy kalit</translation>
 <translation id="7762130827864645708">Parolingiz oʻzgartirildi. Endi yangi paroldan foydalaning.</translation>
+<translation id="7762839321248874531"><ph name="BEGIN_LINK1" />Tizim va ilova axboroti<ph name="END_LINK1" /> hamda <ph name="BEGIN_LINK2" />statistikasini<ph name="END_LINK2" /> yuborish</translation>
 <translation id="7763470514545477072">Domen kengaytmasi topildi</translation>
 <translation id="7769672763586021400">Model identifikatori</translation>
 <translation id="7784116172884276937">DNS serverlar sozlanmagan</translation>
@@ -831,6 +839,7 @@
 <translation id="8382302752385457774">Oq yorliq</translation>
 <translation id="8395584934117017006">Bu <ph name="DEVICE_TYPE" /> korporativ domen boshqaruvida</translation>
 <translation id="8398927464629426868">Qurilmaning quvvat olish yoki sarflash darajasi</translation>
+<translation id="8420955526972171689">Apparatli muammolarni aniqlaydigan testlarni ishga tushirish</translation>
 <translation id="8422748173858722634">IMEI</translation>
 <translation id="8428073762635678092">Ichki klaviatura</translation>
 <translation id="8431300646573772016">Yangilangan ChromeOS</translation>
diff --git a/chromeos/strings/chromeos_strings_zh-HK.xtb b/chromeos/strings/chromeos_strings_zh-HK.xtb
index 6286836..586cc58 100644
--- a/chromeos/strings/chromeos_strings_zh-HK.xtb
+++ b/chromeos/strings/chromeos_strings_zh-HK.xtb
@@ -121,7 +121,7 @@
 <translation id="1871569928317311284">關閉深色主題背景</translation>
 <translation id="1874612839560830905">MTU</translation>
 <translation id="188114911237521550">關閉暗光模式</translation>
-<translation id="1881188606372070653">如要基於法律原因提出內容變更要求,請前往<ph name="BEGIN_LINK1" />法律說明頁面<ph name="END_LINK1" />。系統可能會將部分帳戶和系統資訊傳送給 Google。我們會使用您提供的資訊處理技術問題及改善服務品質,且過程中會遵守《<ph name="BEGIN_LINK2" />隱私權政策<ph name="END_LINK2" />》和《<ph name="BEGIN_LINK3" />服務條款<ph name="END_LINK3" />》。</translation>
+<translation id="1881188606372070653">如因法律理由而需要變更內容,請於<ph name="BEGIN_LINK1" />「法律說明」頁面<ph name="END_LINK1" />提出要求。部分帳戶及系統資料可能會傳送予 Google。我們會根據《<ph name="BEGIN_LINK2" />私隱權政策<ph name="END_LINK2" />》及《<ph name="BEGIN_LINK3" />服務條款<ph name="END_LINK3" />》,利用您提供的資料以解決技術問題和改善服務。</translation>
 <translation id="1887850431809612466">硬件版本</translation>
 <translation id="1905710495812624430">允許的嘗試次數超過上限。</translation>
 <translation id="1908234395526491708">UDP 要求失敗</translation>
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
index f3e66bf7..86a47cb 100644
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
+++ b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
@@ -29,11 +29,13 @@
 #control-panel {
   display: block;
   font-size: 120%;
+  line-height: calc(120% + 2ex);
   padding: 1ex;
 }
 
 #control-panel label {
   padding-inline-end: 1em;
+  white-space: nowrap;
 }
 
 .fake-button {
@@ -274,7 +276,6 @@
   position: absolute;
   right: 20px;
 }
-
 </style>
 </head>
 <body>
@@ -282,7 +283,7 @@
   <h1 id="h1-title"></h1>
   <div id="control-panel">
     <span id="marker-fake-button" class="fake-button">Add Marker</span>
-    <input type="checkbox" id="enable-autoscroll" checked><label for="enable-autoscroll">Enable autoscroll</label>
+    <label><input type="checkbox" id="enable-autoscroll" checked> Enable autoscroll</label>
     <span id="checkbox-placeholder"></span>
     <span id="reset-cache-fake-button" class="fake-button" style="display: none">Reset Cache</span>
     <span id="download-fake-button" class="fake-button">Download Log</span>
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
index 9150342..476572a 100644
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
+++ b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
@@ -168,8 +168,7 @@
 }
 
 function enableResetCacheButton() {
-  document.getElementById('reset-cache-fake-button').style.display =
-      'inline-block';
+  document.getElementById('reset-cache-fake-button').style.display = 'inline';
 }
 
 function notifyAboutIncognito(isIncognito) {
@@ -294,9 +293,8 @@
     input.addEventListener('change', changeHandler);
     changeHandler();  // Call once to initialize |logDiv|'s classes.
     const label = document.createElement('label');
-    label.setAttribute('for', `checkbox-${scope}`);
-    label.innerText = scope;
-    checkboxPlaceholder.appendChild(input);
+    label.appendChild(input);
+    label.appendChild(document.createTextNode(' ' + scope));
     checkboxPlaceholder.appendChild(label);
   }
 }
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index 611d901..07c4c1f 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -406,7 +406,7 @@
     out << Tr{} << "has_form_tag:" << upload.has_form_tag();
 
   if (upload.has_single_username_data()) {
-    LogBuffer single_username_data;
+    LogBuffer single_username_data(LogBuffer::IsActive(true));
     single_username_data << Tag{"span"} << "[";
     single_username_data
         << Tr{} << "username_form_signature:"
@@ -736,12 +736,10 @@
     request_data.payload = std::move(payload);
 
     DVLOG(1) << "Sending Autofill Upload Request:\n" << upload;
-    if (log_manager_) {
-      log_manager_->Log() << LoggingScope::kAutofillServer
-                          << LogMessage::kSendAutofillUpload << Br{}
-                          << "Allow upload?: " << allow_upload << Br{}
-                          << "Data: " << Br{} << upload;
-    }
+    LOG_AF(log_manager_) << LoggingScope::kAutofillServer
+                         << LogMessage::kSendAutofillUpload << Br{}
+                         << "Allow upload?: " << allow_upload << Br{}
+                         << "Data: " << Br{} << upload;
 
     if (!allow_upload)
       return false;
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index 710f91c9..dc2274c5 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -43,19 +43,15 @@
 
 namespace autofill {
 namespace {
-void LogCardUploadDisabled(LogManager* log_manager, std::string context) {
-  if (log_manager) {
-    log_manager->Log() << LoggingScope::kCreditCardUploadStatus
-                       << LogMessage::kCreditCardUploadDisabled << context
-                       << CTag{};
-  }
+void LogCardUploadDisabled(LogManager* log_manager, base::StringPiece context) {
+  LOG_AF(log_manager) << LoggingScope::kCreditCardUploadStatus
+                      << LogMessage::kCreditCardUploadDisabled << context
+                      << CTag{};
 }
 
 void LogCardUploadEnabled(LogManager* log_manager) {
-  if (log_manager) {
-    log_manager->Log() << LoggingScope::kCreditCardUploadStatus
-                       << LogMessage::kCreditCardUploadEnabled << CTag{};
-  }
+  LOG_AF(log_manager) << LoggingScope::kCreditCardUploadStatus
+                      << LogMessage::kCreditCardUploadEnabled << CTag{};
 }
 
 // Given an email account domain, returns the contents before the first dot.
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 858cec0d..26d5e15 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -85,15 +85,12 @@
       VLOG(1) << *form;
   }
 
-  if (!log_manager || !log_manager->IsLoggingActive())
-    return;
-
-  LogBuffer buffer;
+  LogBuffer buffer(IsLoggingActive(log_manager));
   for (FormStructure* form : forms)
-    buffer << *form;
+    LOG_AF(buffer) << *form;
 
-  log_manager->Log() << LoggingScope::kParsing << LogMessage::kParsedForms
-                     << std::move(buffer);
+  LOG_AF(log_manager) << LoggingScope::kParsing << LogMessage::kParsedForms
+                      << std::move(buffer);
 }
 
 // static
@@ -405,10 +402,8 @@
 FormStructure* AutofillManager::ParseForm(const FormData& form,
                                           const FormStructure* cached_form) {
   if (form_structures_.size() >= kAutofillManagerMaxFormCacheSize) {
-    if (log_manager_) {
-      log_manager_->Log() << LoggingScope::kAbortParsing
-                          << LogMessage::kAbortParsingTooManyForms << form;
-    }
+    LOG_AF(log_manager_) << LoggingScope::kAbortParsing
+                         << LogMessage::kAbortParsingTooManyForms << form;
     return nullptr;
   }
 
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index e1205614..b5cf986 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -371,16 +371,6 @@
   }
 }
 
-// Logs the reason for suppressing autofill suggestions to
-// chrome://autofill-internals.
-void LogSuppressReason(LogManager* log_manager, const std::string& reason) {
-  if (!log_manager)
-    return;
-  log_manager->Log() << LoggingScope::kFilling
-                     << LogMessage::kSuggestionSuppressed
-                     << " Reason: " << reason;
-}
-
 }  // namespace
 
 BrowserAutofillManager::FillingContext::FillingContext(
@@ -659,13 +649,11 @@
                                                  SubmissionSource source) {
   base::UmaHistogramEnumeration("Autofill.FormSubmission.PerProfileType",
                                 client()->GetProfileType());
-  if (log_manager()) {
-    log_manager()->Log() << LoggingScope::kSubmission
-                         << LogMessage::kFormSubmissionDetected << Br{}
-                         << "known_success: " << known_success << Br{}
-                         << "source: " << SubmissionSourceToString(source)
-                         << Br{} << form;
-  }
+  LOG_AF(log_manager()) << LoggingScope::kSubmission
+                        << LogMessage::kFormSubmissionDetected << Br{}
+                        << "known_success: " << known_success << Br{}
+                        << "source: " << SubmissionSourceToString(source)
+                        << Br{} << form;
 
   // Always upload page language metrics.
   LogLanguageMetrics(client()->GetLanguageState());
@@ -972,17 +960,25 @@
         single_field_form_fill_router_->CancelPendingQueries(this);
         external_delegate_->OnSuggestionsReturned(query_id, suggestions,
                                                   autoselect_first_suggestion);
-        LogSuppressReason(log_manager(), "Ablation experiment");
+        LOG_AF(log_manager())
+            << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed
+            << " Reason: Ablation experiment";
         return;
 
       case SuppressReason::kInsecureForm:
-        LogSuppressReason(log_manager(), "Insecure form");
+        LOG_AF(log_manager())
+            << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed
+            << " Reason: Insecure form";
         return;
       case SuppressReason::kAutocompleteOff:
-        LogSuppressReason(log_manager(), "autocomplete=off");
+        LOG_AF(log_manager())
+            << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed
+            << " Reason: autocomplete=off";
         return;
       case SuppressReason::kAutocompleteUnrecognized:
-        LogSuppressReason(log_manager(), "autocomplete=unrecognized");
+        LOG_AF(log_manager())
+            << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed
+            << " Reason: autocomplete=unrecognized";
         return;
     }
 
@@ -1506,39 +1502,38 @@
     const std::u16string& old_value) {
   // Log to chrome://autofill-internals that a field's value was set by
   // JavaScript.
-  if (log_manager()) {
-    auto StructureOfString = [](std::u16string str) {
-      for (auto& c : str) {
-        if (base::IsAsciiAlpha(c)) {
-          c = 'a';
-        } else if (base::IsAsciiDigit(c)) {
-          c = '0';
-        } else if (base::IsAsciiWhitespace(c)) {
-          c = ' ';
-        } else {
-          c = '$';
-        }
-      }
-      return str;
-    };
-    std::string field_number = "unknown";
-    for (size_t i = 0; i < form.fields.size(); ++i) {
-      if (form.fields[i].global_id() == field.global_id()) {
-        field_number = base::StringPrintf("Field %zu", i);
+  auto StructureOfString = [](std::u16string str) {
+    for (auto& c : str) {
+      if (base::IsAsciiAlpha(c)) {
+        c = 'a';
+      } else if (base::IsAsciiDigit(c)) {
+        c = '0';
+      } else if (base::IsAsciiWhitespace(c)) {
+        c = ' ';
+      } else {
+        c = '$';
       }
     }
-    LogBuffer change;
-    change << Tag{"div"} << Attrib{"class", "form"};
-    change << field << Br{};
-    change << "Old value structure: '"
-           << StructureOfString(old_value.substr(0, 80)) << "'" << Br{};
-    change << "New value structure: '"
-           << StructureOfString(field.value.substr(0, 80)) << "'";
-    log_manager()->Log() << LoggingScope::kWebsiteModifiedFieldValue
-                         << LogMessage::kJavaScriptChangedAutofilledValue
-                         << Br{} << Tag{"table"} << Tr{} << field_number
-                         << std::move(change);
-  }
+    return str;
+  };
+  auto GetFieldNumber = [&]() {
+    for (size_t i = 0; i < form.fields.size(); ++i) {
+      if (form.fields[i].global_id() == field.global_id())
+        return base::StringPrintf("Field %zu", i);
+    }
+    return std::string("unknown");
+  };
+  LogBuffer change(IsLoggingActive(log_manager()));
+  LOG_AF(change) << Tag{"div"} << Attrib{"class", "form"};
+  LOG_AF(change) << field << Br{};
+  LOG_AF(change) << "Old value structure: '"
+                 << StructureOfString(old_value.substr(0, 80)) << "'" << Br{};
+  LOG_AF(change) << "New value structure: '"
+                 << StructureOfString(field.value.substr(0, 80)) << "'";
+  LOG_AF(log_manager()) << LoggingScope::kWebsiteModifiedFieldValue
+                        << LogMessage::kJavaScriptChangedAutofilledValue << Br{}
+                        << Tag{"table"} << Tr{} << GetFieldNumber()
+                        << std::move(change);
 
   MaybeTriggerRefillForExpirationDate(form, field, old_value);
 }
@@ -1829,11 +1824,11 @@
   DCHECK(form_structure);
   DCHECK(autofill_field);
 
-  LogBuffer buffer;
-  buffer << "is credit card section: " << is_credit_card << Br{};
-  buffer << "is refill: " << is_refill << Br{};
-  buffer << *form_structure << Br{};
-  buffer << Tag{"table"};
+  LogBuffer buffer(IsLoggingActive(log_manager()));
+  LOG_AF(buffer) << "is credit card section: " << is_credit_card << Br{};
+  LOG_AF(buffer) << "is refill: " << is_refill << Br{};
+  LOG_AF(buffer) << *form_structure << Br{};
+  LOG_AF(buffer) << Tag{"table"};
 
   form_structure->RationalizePhoneNumbersInSection(autofill_field->section);
 
@@ -1877,13 +1872,15 @@
     result.fields[i].section = form_structure->field(i)->section;
 
     if (form_structure->field(i)->section != autofill_field->section) {
-      buffer << Tr{} << field_number << "Skipped: not part of filled section";
+      LOG_AF(buffer) << Tr{} << field_number
+                     << "Skipped: not part of filled section";
       continue;
     }
 
     if (form_structure->field(i)->only_fill_when_focused() &&
         !form_structure->field(i)->SameFieldAs(field)) {
-      buffer << Tr{} << field_number << "Skipped: only fill when focused";
+      LOG_AF(buffer) << Tr{} << field_number
+                     << "Skipped: only fill when focused";
       continue;
     }
 
@@ -1903,7 +1900,7 @@
           ->LogHiddenRepresentationalFieldSkipDecision(*form_structure,
                                                        *cached_field, skip);
       if (skip) {
-        buffer << Tr{} << field_number << "Skipped: invisible field";
+        LOG_AF(buffer) << Tr{} << field_number << "Skipped: invisible field";
         continue;
       }
     }
@@ -1953,8 +1950,8 @@
         (!form.fields[i].value.empty() ||
          !form_structure->field(i)->value.empty()) &&
         !cached_field->SameFieldAs(field)) {
-      buffer << Tr{} << field_number
-             << "Skipped: don't fill user-filled fields";
+      LOG_AF(buffer) << Tr{} << field_number
+                     << "Skipped: don't fill user-filled fields";
       continue;
     }
 
@@ -1962,15 +1959,16 @@
     // when it's a refill.
     if (result.fields[i].is_autofilled && !cached_field->SameFieldAs(field) &&
         !is_refill) {
-      buffer << Tr{} << field_number
-             << "Skipped: don't fill previously filled fields unless during a "
-                "refill";
+      LOG_AF(buffer)
+          << Tr{} << field_number
+          << "Skipped: don't fill previously filled fields unless during a "
+             "refill";
       continue;
     }
 
     if (field_group_type == FieldTypeGroup::kNoGroup) {
-      buffer << Tr{} << field_number
-             << "Skipped: field type has no fillable group";
+      LOG_AF(buffer) << Tr{} << field_number
+                     << "Skipped: field type has no fillable group";
       continue;
     }
 
@@ -1979,9 +1977,10 @@
     if (is_refill &&
         !base::Contains(filling_context->type_groups_originally_filled,
                         field_group_type)) {
-      buffer << Tr{} << field_number
-             << "Skipped: in a refill, only fields from the group that was "
-                "filled in the initial fill may be filled";
+      LOG_AF(buffer)
+          << Tr{} << field_number
+          << "Skipped: in a refill, only fields from the group that was "
+             "filled in the initial fill may be filled";
       continue;
     }
 
@@ -1991,16 +1990,16 @@
     if (data_util::IsCreditCardExpirationType(field_type) &&
         (is_credit_card && absl::get<const CreditCard*>(profile_or_credit_card)
                                ->IsExpired(AutofillClock::Now()))) {
-      buffer << Tr{} << field_number
-             << "Skipped: don't fill expiration date of expired cards";
+      LOG_AF(buffer) << Tr{} << field_number
+                     << "Skipped: don't fill expiration date of expired cards";
       continue;
     }
 
     // A field with a specific type is only allowed to be filled a limited
     // number of times given by |TypeValueFormFillingLimit(field_type)|.
     if (++type_count[field_type] > TypeValueFormFillingLimit(field_type)) {
-      buffer << Tr{} << field_number
-             << "Skipped: field-type filling-limit reached";
+      LOG_AF(buffer) << Tr{} << field_number
+                     << "Skipped: field-type filling-limit reached";
       continue;
     }
 
@@ -2042,16 +2041,17 @@
     bool has_value_after = !result.fields[i].value.empty();
     bool is_autofilled_after = result.fields[i].is_autofilled;
 
-    buffer << Tr{} << field_number
-           << base::StringPrintf(
-                  "Fillable - has value: %d->%d; autofilled: %d->%d. %s",
-                  has_value_before, has_value_after, is_autofilled_before,
-                  is_autofilled_after, failure_to_fill.c_str());
+    LOG_AF(buffer)
+        << Tr{} << field_number
+        << base::StringPrintf(
+               "Fillable - has value: %d->%d; autofilled: %d->%d. %s",
+               has_value_before, has_value_after, is_autofilled_before,
+               is_autofilled_after, failure_to_fill.c_str());
 
     if (!cached_field->IsFocusable() && result.fields[i].is_autofilled)
       AutofillMetrics::LogHiddenOrPresentationalSelectFieldsFilled();
   }
-  buffer << CTag{"table"};
+  LOG_AF(buffer) << CTag{"table"};
 
   autofilled_form_signatures_.push_front(form_structure->FormSignatureAsStr());
   // Only remember the last few forms that we've seen, both to avoid false
@@ -2059,11 +2059,9 @@
   if (autofilled_form_signatures_.size() > kMaxRecentFormSignaturesToRemember)
     autofilled_form_signatures_.pop_back();
 
-  if (log_manager()) {
-    log_manager()->Log() << LoggingScope::kFilling
-                         << LogMessage::kSendFillingData << Br{}
-                         << std::move(buffer);
-  }
+  LOG_AF(log_manager()) << LoggingScope::kFilling
+                        << LogMessage::kSendFillingData << Br{}
+                        << std::move(buffer);
 
   auto field_types = base::MakeFlatMap<FieldGlobalId, ServerFieldType>(
       *form_structure, {}, [](const auto& field) {
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index e87e427..32f7872 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -80,23 +80,19 @@
            ? field_type_group != FieldTypeGroup::kPhoneBilling &&
                  field_type_group != FieldTypeGroup::kPhoneHome
            : field_type != PHONE_HOME_NUMBER)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Multiple fields of type "
-                         << AutofillType::ServerFieldTypeToString(field_type)
-                         << "." << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Multiple fields of type "
+        << AutofillType::ServerFieldTypeToString(field_type) << "." << CTag{};
     return false;
   }
   // Abandon the import if an email address value shows up in a field that is
   // not an email address.
   if (field_type != EMAIL_ADDRESS && IsValidEmailAddress(value)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Email address found in field of different type: "
-                         << AutofillType::ServerFieldTypeToString(field_type)
-                         << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Email address found in field of different type: "
+        << AutofillType::ServerFieldTypeToString(field_type) << CTag{};
     return false;
   }
 
@@ -116,47 +112,41 @@
   AutofillCountry country(predicted_country_code, app_locale);
 
   // Include the details of the country to the log.
-  if (import_log_buffer)
-    *import_log_buffer << country;
+  LOG_AF(import_log_buffer) << country;
 
   // Check the |ADDRESS_HOME_LINE1| requirement.
   bool is_line1_missing = false;
   if (country.requires_line1() && !profile.HasRawInfo(ADDRESS_HOME_LINE1) &&
       !profile.HasRawInfo(ADDRESS_HOME_STREET_NAME)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Missing required ADDRESS_HOME_LINE1." << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Missing required ADDRESS_HOME_LINE1." << CTag{};
     is_line1_missing = true;
   }
 
   // Check the |ADDRESS_HOME_CITY| requirement.
   bool is_city_missing = false;
   if (country.requires_city() && !profile.HasRawInfo(ADDRESS_HOME_CITY)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Missing required ADDRESS_HOME_CITY." << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Missing required ADDRESS_HOME_CITY." << CTag{};
     is_city_missing = true;
   }
 
   // Check the |ADDRESS_HOME_STATE| requirement.
   bool is_state_missing = false;
   if (country.requires_state() && !profile.HasRawInfo(ADDRESS_HOME_STATE)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Missing required ADDRESS_HOME_STATE." << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Missing required ADDRESS_HOME_STATE." << CTag{};
     is_state_missing = true;
   }
 
   // Check the |ADDRESS_HOME_ZIP| requirement.
   bool is_zip_missing = false;
   if (country.requires_zip() && !profile.HasRawInfo(ADDRESS_HOME_ZIP)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Missing required ADDRESS_HOME_ZIP." << CTag{};
-    }
+    LOG_AF(import_log_buffer) << LogMessage::kImportAddressProfileFromFormFailed
+                              << "Missing required ADDRESS_HOME_ZIP." << CTag{};
     is_zip_missing = true;
   }
 
@@ -164,12 +154,9 @@
   if (country.requires_zip_or_state() &&
       !profile.HasRawInfo(ADDRESS_HOME_ZIP) &&
       !profile.HasRawInfo(ADDRESS_HOME_STATE)) {
-    if (import_log_buffer) {
-      *import_log_buffer
-          << LogMessage::kImportAddressProfileFromFormFailed
-          << "Missing required ADDRESS_HOME_ZIP or ADDRESS_HOME_STATE."
-          << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Missing required ADDRESS_HOME_ZIP or ADDRESS_HOME_STATE." << CTag{};
     is_zip_or_state_requirement_violated = true;
   }
 
@@ -177,12 +164,10 @@
   if (country.requires_line1_or_house_number() &&
       !profile.HasRawInfo(ADDRESS_HOME_LINE1) &&
       !profile.HasRawInfo(ADDRESS_HOME_HOUSE_NUMBER)) {
-    if (import_log_buffer) {
-      *import_log_buffer
-          << LogMessage::kImportAddressProfileFromFormFailed
-          << "Missing required ADDRESS_HOME_LINE1 or ADDRESS_HOME_HOUSE_NUMBER."
-          << CTag{};
-    }
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Missing required ADDRESS_HOME_LINE1 or ADDRESS_HOME_HOUSE_NUMBER."
+        << CTag{};
     is_line1_or_house_number_violated = true;
   }
 
@@ -352,32 +337,28 @@
   bool is_email_invalid = false;
   std::u16string email = profile.GetRawInfo(EMAIL_ADDRESS);
   if (!email.empty() && !IsValidEmailAddress(email)) {
-    if (import_log_buffer) {
-      *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                         << "Invalid email address." << CTag{};
-    }
+    LOG_AF(import_log_buffer) << LogMessage::kImportAddressProfileFromFormFailed
+                              << "Invalid email address." << CTag{};
     is_email_invalid = true;
   }
 
   // Reject profiles with an invalid |HOME_ADDRESS_STATE| entry.
   bool is_state_invalid = false;
   if (profile.IsPresentButInvalid(ADDRESS_HOME_STATE)) {
-    if (import_log_buffer)
-      *import_log_buffer
-          << LogMessage::kImportAddressProfileFromFormFailed
-          << "Invalid state as of AutofillProfile::IsPresentButInvalid()."
-          << CTag{};
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Invalid state as of AutofillProfile::IsPresentButInvalid()."
+        << CTag{};
     is_state_invalid = true;
   }
 
   // Reject profiles with an invalid |HOME_ADDRESS_ZIP| entry.
   bool is_zip_invalid = false;
   if (profile.IsPresentButInvalid(ADDRESS_HOME_ZIP)) {
-    if (import_log_buffer)
-      *import_log_buffer
-          << LogMessage::kImportAddressProfileFromFormFailed
-          << "Invalid ZIP as of AutofillProfile::IsPresentButInvalid()."
-          << CTag{};
+    LOG_AF(import_log_buffer)
+        << LogMessage::kImportAddressProfileFromFormFailed
+        << "Invalid ZIP as of AutofillProfile::IsPresentButInvalid()."
+        << CTag{};
     is_zip_invalid = true;
   }
 
@@ -528,11 +509,12 @@
     const FormStructure& form,
     std::vector<AddressProfileImportCandidate>& import_candidates) {
   // Create a buffer to collect logging output for the autofill-internals.
-  LogBuffer import_log_buffer;
-  import_log_buffer << LoggingScope::kAddressProfileFormImport;
+  LogManager* log_manager = client_->GetLogManager();
+  LogBuffer import_log_buffer(IsLoggingActive(log_manager));
+  LOG_AF(import_log_buffer) << LoggingScope::kAddressProfileFormImport;
   // Print the full form into the logging scope.
-  import_log_buffer << LogMessage::kImportAddressProfileFromForm << form
-                    << CTag{};
+  LOG_AF(import_log_buffer)
+      << LogMessage::kImportAddressProfileFromForm << form << CTag{};
 
   // We save a maximum of 2 profiles per submitted form (e.g. for shipping and
   // billing).
@@ -540,8 +522,8 @@
   size_t num_complete_profiles = 0;
 
   if (!form.field_count()) {
-    import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                      << "Form is empty." << CTag{};
+    LOG_AF(import_log_buffer) << LogMessage::kImportAddressProfileFromFormFailed
+                              << "Form is empty." << CTag{};
   } else {
     // Relevant sections for address fields.
     std::set<std::string> sections;
@@ -554,17 +536,18 @@
       if (num_complete_profiles == kMaxNumAddressProfilesSaved)
         break;
       // Log the output from a section in a separate div for readability.
-      import_log_buffer << Tag{"div"}
-                        << Attrib{"class", "profile_import_from_form_section"};
-      import_log_buffer << LogMessage::kImportAddressProfileFromFormSection
-                        << section << CTag{};
+      LOG_AF(import_log_buffer)
+          << Tag{"div"} << Attrib{"class", "profile_import_from_form_section"};
+      LOG_AF(import_log_buffer)
+          << LogMessage::kImportAddressProfileFromFormSection << section
+          << CTag{};
       // Try to import an address profile from the form fields of this section.
       // Only allow for a prompt if no other complete profile was found so far.
       if (ImportAddressProfileForSection(form, section, import_candidates,
                                          &import_log_buffer))
         num_complete_profiles++;
       // And close the div of the section import log.
-      import_log_buffer << CTag{"div"};
+      LOG_AF(import_log_buffer) << CTag{"div"};
     }
     // Run the import on the union of the section if the import was not
     // successful and if there is more than one section.
@@ -586,13 +569,12 @@
           AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT);
     }
   }
-  import_log_buffer << LogMessage::kImportAddressProfileFromFormNumberOfImports
-                    << num_complete_profiles << CTag{};
+  LOG_AF(import_log_buffer)
+      << LogMessage::kImportAddressProfileFromFormNumberOfImports
+      << num_complete_profiles << CTag{};
 
   // Write log buffer to autofill-internals.
-  LogManager* log_manager = client_->GetLogManager();
-  if (log_manager)
-    log_manager->Log() << std::move(import_log_buffer);
+  LOG_AF(log_manager) << std::move(import_log_buffer);
 
   return num_complete_profiles > 0;
 }
@@ -673,11 +655,9 @@
     if (server_field_type == EMAIL_ADDRESS &&
         types_seen.count(server_field_type) &&
         candidate_profile.GetRawInfo(EMAIL_ADDRESS) != value) {
-      if (import_log_buffer) {
-        *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                           << "Multiple different email addresses present."
-                           << CTag{};
-      }
+      LOG_AF(import_log_buffer)
+          << LogMessage::kImportAddressProfileFromFormFailed
+          << "Multiple different email addresses present." << CTag{};
       has_multiple_distinct_email_addresses = true;
     }
 
@@ -737,10 +717,9 @@
       }
       // Check if the country code was still not determined correctly.
       if (!candidate_profile.HasRawInfo(ADDRESS_HOME_COUNTRY)) {
-        if (import_log_buffer) {
-          *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                             << "Missing country." << CTag{};
-        }
+        LOG_AF(import_log_buffer)
+            << LogMessage::kImportAddressProfileFromFormFailed
+            << "Missing country." << CTag{};
         has_invalid_country = true;
       }
     }
@@ -760,10 +739,9 @@
       import_metadata.did_remove_invalid_phone_number = true;
     } else {
       has_invalid_phone_number = true;
-      if (import_log_buffer) {
-        *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
-                           << "Invalid phone number." << CTag{};
-      }
+      LOG_AF(import_log_buffer)
+          << LogMessage::kImportAddressProfileFromFormFailed
+          << "Invalid phone number." << CTag{};
     }
   }
 
diff --git a/components/autofill/core/browser/form_parsing/form_field.cc b/components/autofill/core/browser/form_parsing/form_field.cc
index 9cd4d36..8a8c0c58 100644
--- a/components/autofill/core/browser/form_parsing/form_field.cc
+++ b/components/autofill/core/browser/form_parsing/form_field.cc
@@ -119,27 +119,26 @@
                  candidate.second.BestHeuristicType() == MERCHANT_PROMO_CODE);
       });
     } else {
-      if (log_manager) {
-        LogBuffer table_rows;
-        for (const auto& field : fields) {
-          table_rows << Tr{} << "Field:" << *field;
-        }
-        for (const auto& [field_id, candidates] : field_candidates) {
-          LogBuffer name;
-          name << "Type candidate for frame and renderer ID: " << field_id;
-          LogBuffer description;
-          ServerFieldType field_type = candidates.BestHeuristicType();
-          description << "BestHeuristicType: "
-                      << AutofillType::ServerFieldTypeToString(field_type)
-                      << ", is fillable: " << IsFillableFieldType(field_type);
-          table_rows << Tr{} << std::move(name) << std::move(description);
-        }
-        log_manager->Log()
-            << LoggingScope::kParsing
-            << LogMessage::kLocalHeuristicDidNotFindEnoughFillableFields
-            << Tag{"table"} << Attrib{"class", "form"} << std::move(table_rows)
-            << CTag{"table"};
+      LogBuffer table_rows(IsLoggingActive(log_manager));
+      for (const auto& field : fields)
+        LOG_AF(table_rows) << Tr{} << "Field:" << *field;
+      for (const auto& [field_id, candidates] : field_candidates) {
+        LogBuffer name(IsLoggingActive(log_manager));
+        name << "Type candidate for frame and renderer ID: " << field_id;
+        LogBuffer description(IsLoggingActive(log_manager));
+        LOG_AF(description)
+            << "BestHeuristicType: "
+            << AutofillType::ServerFieldTypeToString(
+                   candidates.BestHeuristicType())
+            << ", is fillable: "
+            << IsFillableFieldType(candidates.BestHeuristicType());
+        LOG_AF(table_rows) << Tr{} << std::move(name) << std::move(description);
       }
+      LOG_AF(log_manager)
+          << LoggingScope::kParsing
+          << LogMessage::kLocalHeuristicDidNotFindEnoughFillableFields
+          << Tag{"table"} << Attrib{"class", "form"} << std::move(table_rows)
+          << CTag{"table"};
       field_candidates.clear();
     }
   }
@@ -363,15 +362,16 @@
     value = field->placeholder;
   }
 
-  if (found_match && logging.log_manager) {
-    LogBuffer table_rows;
-    table_rows << Tr{} << "Match type:" << match_type_string;
-    table_rows << Tr{} << "RegEx:" << logging.regex_name;
-    table_rows << Tr{} << "Value: " << HighlightValue(value, matches[0]);
+  if (found_match) {
+    LogBuffer table_rows(IsLoggingActive(logging.log_manager));
+    LOG_AF(table_rows) << Tr{} << "Match type:" << match_type_string;
+    LOG_AF(table_rows) << Tr{} << "RegEx:" << logging.regex_name;
+    LOG_AF(table_rows) << Tr{}
+                       << "Value: " << HighlightValue(value, matches[0]);
     // The matched substring is reported once more as the highlighting is not
     // particularly copy&paste friendly.
-    table_rows << Tr{} << "Matched substring: " << matches[0];
-    logging.log_manager->Log()
+    LOG_AF(table_rows) << Tr{} << "Matched substring: " << matches[0];
+    LOG_AF(logging.log_manager)
         << LoggingScope::kParsing << LogMessage::kLocalHeuristicRegExMatched
         << Tag{"table"} << std::move(table_rows) << CTag{"table"};
   }
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 2093107..75a7905d 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -506,14 +506,6 @@
   }
 }
 
-LogBufferSubmitter LogRationalization(LogManager* log_manager) {
-  if (!log_manager)
-    return LogManager::DevNull();
-  LogBufferSubmitter submitter = log_manager->Log();
-  submitter << LoggingScope::kRationalization << LogMessage::kRationalization;
-  return submitter;
-}
-
 // Creates a unique name for the section that starts with |field|.
 //
 // The section name is a string of the form "%s_%u_%u", where the first string
@@ -820,10 +812,8 @@
   using FieldSuggestion =
       AutofillQueryResponse::FormSuggestion::FieldSuggestion;
   AutofillMetrics::LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED);
-  if (log_manager) {
-    log_manager->Log() << LoggingScope::kParsing
-                       << LogMessage::kProcessingServerData;
-  }
+  LOG_AF(log_manager) << LoggingScope::kParsing
+                      << LogMessage::kProcessingServerData;
 
   bool heuristics_detected_fillable_field = false;
   bool query_response_overrode_heuristics = false;
@@ -1028,10 +1018,8 @@
 bool FormStructure::ShouldBeParsed(LogManager* log_manager) const {
   // Exclude URLs not on the web via HTTP(S).
   if (!HasAllowedScheme(source_url_)) {
-    if (log_manager) {
-      log_manager->Log() << LoggingScope::kAbortParsing
-                         << LogMessage::kAbortParsingNotAllowedScheme << *this;
-    }
+    LOG_AF(log_manager) << LoggingScope::kAbortParsing
+                        << LogMessage::kAbortParsingNotAllowedScheme << *this;
     return false;
   }
 
@@ -1042,22 +1030,18 @@
       (!all_fields_are_passwords() ||
        active_field_count() < kRequiredFieldsForFormsWithOnlyPasswordFields) &&
       !has_author_specified_types_) {
-    if (log_manager) {
-      log_manager->Log() << LoggingScope::kAbortParsing
-                         << LogMessage::kAbortParsingNotEnoughFields
-                         << active_field_count() << *this;
-    }
+    LOG_AF(log_manager) << LoggingScope::kAbortParsing
+                        << LogMessage::kAbortParsingNotEnoughFields
+                        << active_field_count() << *this;
     return false;
   }
 
   // Rule out search forms.
   if (MatchesPattern(base::UTF8ToUTF16(target_url_.path_piece()),
                      kUrlSearchActionRe)) {
-    if (log_manager) {
-      log_manager->Log() << LoggingScope::kAbortParsing
-                         << LogMessage::kAbortParsingUrlMatchesSearchRegex
-                         << *this;
-    }
+    LOG_AF(log_manager) << LoggingScope::kAbortParsing
+                        << LogMessage::kAbortParsingUrlMatchesSearchRegex
+                        << *this;
     return false;
   }
 
@@ -1066,9 +1050,9 @@
     has_text_field |= it->form_control_type != "select-one";
   }
 
-  if (!has_text_field && log_manager) {
-    log_manager->Log() << LoggingScope::kAbortParsing
-                       << LogMessage::kAbortParsingFormHasNoTextfield << *this;
+  if (!has_text_field) {
+    LOG_AF(log_manager) << LoggingScope::kAbortParsing
+                        << LogMessage::kAbortParsingFormHasNoTextfield << *this;
   }
 
   return has_text_field;
@@ -1751,7 +1735,8 @@
       cc_num_found || num_cc_fields_found >= 3 || num_other_fields_found == 0;
 
   if (!keep_cc_fields && num_cc_fields_found && log_manager) {
-    LogRationalization(log_manager)
+    LOG_AF(log_manager)
+        << LoggingScope::kRationalization << LogMessage::kRationalization
         << "Credit card rationalization: Did not find credit card number, did "
            "not find >= 3 credit card fields ("
         << num_cc_fields_found << "), and had non-cc fields ("
@@ -1798,7 +1783,9 @@
         //       month field(s) not immediately preceding an expiry year field.
         if (!keep_cc_fields || !cc_date_found) {
           if (!cc_date_found && log_manager) {
-            LogRationalization(log_manager)
+            LOG_AF(log_manager)
+                << LoggingScope::kRationalization
+                << LogMessage::kRationalization
                 << "Credit card rationalization: Found CC expiration month but "
                    "not a full date.";
           }
@@ -1807,7 +1794,9 @@
           auto it2 = it + 1;
           if (it2 == fields_.end()) {
             field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
-            LogRationalization(log_manager)
+            LOG_AF(log_manager)
+                << LoggingScope::kRationalization
+                << LogMessage::kRationalization
                 << "Credit card rationalization: Found multiple expiration "
                    "months and the last field was an expiration month";
             field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
@@ -1817,7 +1806,9 @@
                 next_field_type != CREDIT_CARD_EXP_4_DIGIT_YEAR) {
               field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
             }
-            LogRationalization(log_manager)
+            LOG_AF(log_manager)
+                << LoggingScope::kRationalization
+                << LogMessage::kRationalization
                 << "Credit card rationalization: Found multiple expiration "
                    "months and the field following one is not an "
                    "expiration year but "
@@ -1830,7 +1821,9 @@
         if (!keep_cc_fields || !cc_date_found) {
           field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
           if (!cc_date_found && log_manager) {
-            LogRationalization(log_manager)
+            LOG_AF(log_manager)
+                << LoggingScope::kRationalization
+                << LogMessage::kRationalization
                 << "Credit card rationalization: Found expiration year but no "
                    "full expriration date.";
           }
@@ -1864,7 +1857,8 @@
       continue;
     }
     if (log_manager) {
-      LogRationalization(log_manager)
+      LOG_AF(log_manager)
+          << LoggingScope::kRationalization << LogMessage::kRationalization
           << "Street Address Rationalization: Converting sequence of (street "
              "address, address line 2) to (address line 1, address line 2)";
     }
@@ -1919,24 +1913,30 @@
 
     int nb_address_rationalized = 0;
     for (auto field_index : *current_section) {
-      LogBufferSubmitter log_submitter = LogRationalization(log_manager);
-      log_submitter
+      LOG_AF(log_manager)
+          << LoggingScope::kRationalization << LogMessage::kRationalization
           << "RationalizeAddressLineFields ADDRESS_HOME_STREET_ADDRESS to ";
       switch (nb_address_rationalized) {
         case 0:
           ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE1,
                                              form_interactions_ukm_logger);
-          log_submitter << "ADDRESS_HOME_LINE1";
+          LOG_AF(log_manager)
+              << LoggingScope::kRationalization << LogMessage::kRationalization
+              << "ADDRESS_HOME_LINE1";
           break;
         case 1:
           ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE2,
                                              form_interactions_ukm_logger);
-          log_submitter << "ADDRESS_HOME_LINE2";
+          LOG_AF(log_manager)
+              << LoggingScope::kRationalization << LogMessage::kRationalization
+              << "ADDRESS_HOME_LINE2";
           break;
         case 2:
           ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE3,
                                              form_interactions_ukm_logger);
-          log_submitter << "ADDRESS_HOME_LINE3";
+          LOG_AF(log_manager)
+              << LoggingScope::kRationalization << LogMessage::kRationalization
+              << "ADDRESS_HOME_LINE3";
           break;
         default:
           NOTREACHED();
@@ -2112,7 +2112,8 @@
       ApplyRationalizationsToFields(
           upper_index, lower_index, fields_[upper_index]->heuristic_type(),
           fields_[lower_index]->heuristic_type(), form_interactions_ukm_logger);
-      LogRationalization(log_manager)
+      LOG_AF(log_manager)
+          << LoggingScope::kRationalization << LogMessage::kRationalization
           << "RationalizeAddressStateCountry: Heuristics are applicable";
       continue;
     }
@@ -2121,14 +2122,18 @@
       ApplyRationalizationsToFields(upper_index, lower_index,
                                     ADDRESS_HOME_COUNTRY, ADDRESS_HOME_STATE,
                                     form_interactions_ukm_logger);
-      LogRationalization(log_manager) << "RationalizeAddressStateCountry: "
-                                         "FieldShouldBeRationalizedToCountry";
+      LOG_AF(log_manager) << LoggingScope::kRationalization
+                          << LogMessage::kRationalization
+                          << "RationalizeAddressStateCountry: "
+                             "FieldShouldBeRationalizedToCountry";
     } else {
       ApplyRationalizationsToFields(upper_index, lower_index,
                                     ADDRESS_HOME_STATE, ADDRESS_HOME_COUNTRY,
                                     form_interactions_ukm_logger);
-      LogRationalization(log_manager) << "RationalizeAddressStateCountry: "
-                                         "!FieldShouldBeRationalizedToCountry";
+      LOG_AF(log_manager) << LoggingScope::kRationalization
+                          << LogMessage::kRationalization
+                          << "RationalizeAddressStateCountry: "
+                             "!FieldShouldBeRationalizedToCountry";
     }
   }
 }
@@ -2715,7 +2720,7 @@
       // We have relationship rules for this type, but no `neccessary_type` was
       // found. Disabling Autofill for this field.
       field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
-      LogRationalization(log_manager)
+      LOG_AF(log_manager)
           << "RationalizeTypeRelationships: Fields of type "
           << FieldTypeToStringPiece(field_type)
           << " can only exist if other fields of specific types exist.";
diff --git a/components/autofill/core/browser/logging/log_buffer_submitter.cc b/components/autofill/core/browser/logging/log_buffer_submitter.cc
index 7ef67ffe..884b7eab 100644
--- a/components/autofill/core/browser/logging/log_buffer_submitter.cc
+++ b/components/autofill/core/browser/logging/log_buffer_submitter.cc
@@ -9,17 +9,21 @@
 namespace autofill {
 
 LogBufferSubmitter::LogBufferSubmitter(LogRouter* destination, bool active)
-    : destination_(destination) {
-  buffer_.set_active(destination != nullptr && active);
-}
+    : destination_(destination),
+      buffer_(LogBuffer::IsActive(destination != nullptr && active)),
+      destruct_with_logging_(buffer_.active()) {}
 
-LogBufferSubmitter::LogBufferSubmitter(LogBufferSubmitter&& that) noexcept {
-  operator=(std::move(that));
+LogBufferSubmitter::LogBufferSubmitter(LogBufferSubmitter&& that) noexcept
+    : destination_(std::move(that.destination_)),
+      buffer_(std::move(that.buffer_)),
+      destruct_with_logging_(std::move(that.destruct_with_logging_)) {
+  that.destruct_with_logging_ = false;
 }
 
 LogBufferSubmitter& LogBufferSubmitter::operator=(LogBufferSubmitter&& that) {
-  destination_ = that.destination_;
+  destination_ = std::move(that.destination_);
   buffer_ = std::move(that.buffer_);
+  destruct_with_logging_ = std::move(that.destruct_with_logging_);
   that.destruct_with_logging_ = false;
   return *this;
 }
diff --git a/components/autofill/core/browser/logging/log_manager.h b/components/autofill/core/browser/logging/log_manager.h
index b06561165..abbc1987 100644
--- a/components/autofill/core/browser/logging/log_manager.h
+++ b/components/autofill/core/browser/logging/log_manager.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 #include <string>
+#include <type_traits>
 
 #include "base/callback.h"
 #include "components/autofill/core/browser/logging/log_buffer_submitter.h"
+#include "components/autofill/core/common/logging/log_macros.h"
 
 namespace base {
 class Value;
@@ -61,6 +63,44 @@
   static LogBufferSubmitter DevNull();
 };
 
+inline LogBuffer::IsActive IsLoggingActive(LogManager* log_manager) {
+  return LogBuffer::IsActive(log_manager && log_manager->IsLoggingActive());
+}
+
+namespace internal {
+
+// Traits for LOG_AF() macro for `LogManager*`.
+template <typename T>
+struct LoggerTraits<
+    T,
+    typename std::enable_if_t<std::is_convertible_v<decltype(std::declval<T>()),
+                                                    const LogManager*>>> {
+  static bool active(const LogManager* log_manager) {
+    return log_manager && log_manager->IsLoggingActive();
+  }
+
+  static LogBufferSubmitter get_stream(LogManager* log_manager) {
+    return log_manager->Log();
+  }
+};
+
+// Traits for LOG_AF() macro for `LogManager&`.
+template <typename T>
+struct LoggerTraits<
+    T,
+    typename std::enable_if_t<std::is_convertible_v<decltype(std::declval<T>()),
+                                                    const LogManager&>>> {
+  static bool active(const LogManager& log_manager) {
+    return log_manager.IsLoggingActive();
+  }
+
+  static LogBufferSubmitter get_stream(LogManager& log_manager) {
+    return log_manager.Log();
+  }
+};
+
+}  // namespace internal
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_MANAGER_H_
diff --git a/components/autofill/core/browser/logging/log_router.cc b/components/autofill/core/browser/logging/log_router.cc
index 52ebb49..daa0be99 100644
--- a/components/autofill/core/browser/logging/log_router.cc
+++ b/components/autofill/core/browser/logging/log_router.cc
@@ -19,7 +19,7 @@
 
 // static
 base::Value LogRouter::CreateEntryForText(const std::string& text) {
-  LogBuffer buffer;
+  LogBuffer buffer(LogBuffer::IsActive(true));
   buffer << Tag{"div"};
   for (const auto& line : base::SplitStringPiece(
            text, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
diff --git a/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc b/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc
index f4e06852..d76b215 100644
--- a/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc
+++ b/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc
@@ -257,11 +257,11 @@
 }
 
 void FormEventLoggerBase::RecordFunnelAndKeyMetrics() {
-  LogBuffer funnel_rows;
-  LogBuffer key_metrics_rows;
+  LogBuffer funnel_rows(IsLoggingActive(log_manager_));
+  LogBuffer key_metrics_rows(IsLoggingActive(log_manager_));
 
-  funnel_rows << Tr{} << "Form Type: " << form_type_name_;
-  key_metrics_rows << Tr{} << "Form Type: " << form_type_name_;
+  LOG_AF(funnel_rows) << Tr{} << "Form Type: " << form_type_name_;
+  LOG_AF(key_metrics_rows) << Tr{} << "Form Type: " << form_type_name_;
 
   UmaHistogramBoolean("Autofill.Funnel.ParsedAsType." + form_type_name_,
                       has_parsed_form_);
@@ -271,28 +271,29 @@
   UmaHistogramBoolean(
       "Autofill.Funnel.InteractionAfterParsedAsType." + form_type_name_,
       has_logged_interacted_);
-  funnel_rows << Tr{} << "InteractionAfterParsedAsType"
-              << has_logged_interacted_;
+  LOG_AF(funnel_rows) << Tr{} << "InteractionAfterParsedAsType"
+                      << has_logged_interacted_;
   if (has_logged_interacted_) {
     UmaHistogramBoolean(
         "Autofill.Funnel.SuggestionAfterInteraction." + form_type_name_,
         has_logged_suggestions_shown_);
-    funnel_rows << Tr{} << "SuggestionAfterInteraction"
-                << has_logged_suggestions_shown_;
+    LOG_AF(funnel_rows) << Tr{} << "SuggestionAfterInteraction"
+                        << has_logged_suggestions_shown_;
   }
   if (has_logged_interacted_ && has_logged_suggestions_shown_) {
     UmaHistogramBoolean(
         "Autofill.Funnel.FillAfterSuggestion." + form_type_name_,
         has_logged_suggestion_filled_);
-    funnel_rows << Tr{} << "FillAfterSuggestion"
-                << has_logged_suggestion_filled_;
+    LOG_AF(funnel_rows) << Tr{} << "FillAfterSuggestion"
+                        << has_logged_suggestion_filled_;
   }
   if (has_logged_interacted_ && has_logged_suggestions_shown_ &&
       has_logged_suggestion_filled_) {
     UmaHistogramBoolean(
         "Autofill.Funnel.SubmissionAfterFill." + form_type_name_,
         has_logged_will_submit_);
-    funnel_rows << Tr{} << "SubmissionAfterFill" << has_logged_will_submit_;
+    LOG_AF(funnel_rows) << Tr{} << "SubmissionAfterFill"
+                        << has_logged_will_submit_;
   }
   // Log key success metrics, always preconditioned on a form submission (except
   // for the Autofill.KeyMetrics.FormSubmission metrics which measure whether
@@ -305,16 +306,16 @@
     UmaHistogramBoolean(
         "Autofill.KeyMetrics.FillingReadiness." + form_type_name_,
         has_logged_data_to_fill_available_);
-    key_metrics_rows << Tr{} << "FillingReadiness"
-                     << has_logged_data_to_fill_available_;
+    LOG_AF(key_metrics_rows)
+        << Tr{} << "FillingReadiness" << has_logged_data_to_fill_available_;
     if (has_logged_suggestions_shown_) {
       // Whether a user accepted a filling suggestion they saw for a form that
       // was later submitted.
       UmaHistogramBoolean(
           "Autofill.KeyMetrics.FillingAcceptance." + form_type_name_,
           has_logged_suggestion_filled_);
-      key_metrics_rows << Tr{} << "FillingAcceptance"
-                       << has_logged_suggestion_filled_;
+      LOG_AF(key_metrics_rows)
+          << Tr{} << "FillingAcceptance" << has_logged_suggestion_filled_;
       UmaHistogramBoolean(
           base::StrCat({"Autofill.Autocomplete.",
                         (has_logged_autocomplete_off_ ? "Off" : "NotOff"),
@@ -327,15 +328,15 @@
       UmaHistogramBoolean(
           "Autofill.KeyMetrics.FillingCorrectness." + form_type_name_,
           !has_logged_edited_autofilled_field_);
-      key_metrics_rows << Tr{} << "FillingCorrectness"
-                       << !has_logged_edited_autofilled_field_;
+      LOG_AF(key_metrics_rows) << Tr{} << "FillingCorrectness"
+                               << !has_logged_edited_autofilled_field_;
     }
     // Whether a submitted form was filled.
     UmaHistogramBoolean(
         "Autofill.KeyMetrics.FillingAssistance." + form_type_name_,
         has_logged_suggestion_filled_);
-    key_metrics_rows << Tr{} << "FillingAssistance"
-                     << has_logged_suggestion_filled_;
+    LOG_AF(key_metrics_rows)
+        << Tr{} << "FillingAssistance" << has_logged_suggestion_filled_;
 
     if (form_interactions_ukm_logger_) {
       form_interactions_ukm_logger_->LogKeyMetrics(
@@ -353,20 +354,18 @@
              (has_logged_suggestion_filled_ ? "Autofilled." : "NotAutofilled."),
              form_type_name_}),
         has_logged_will_submit_);
-    key_metrics_rows << Tr{} << "FormSubmission.Autofilled"
-                     << has_logged_suggestion_filled_;
-    key_metrics_rows << Tr{} << "FormSubmission.Submission"
-                     << has_logged_will_submit_;
+    LOG_AF(key_metrics_rows)
+        << Tr{} << "FormSubmission.Autofilled" << has_logged_suggestion_filled_;
+    LOG_AF(key_metrics_rows)
+        << Tr{} << "FormSubmission.Submission" << has_logged_will_submit_;
   }
 
-  if (log_manager_) {
-    log_manager_->Log() << LoggingScope::kMetrics << LogMessage::kFunnelMetrics
-                        << Tag{"table"} << std::move(funnel_rows)
-                        << CTag{"table"};
-    log_manager_->Log() << LoggingScope::kMetrics << LogMessage::kKeyMetrics
-                        << Tag{"table"} << std::move(key_metrics_rows)
-                        << CTag{"table"};
-  }
+  LOG_AF(log_manager_) << LoggingScope::kMetrics << LogMessage::kFunnelMetrics
+                       << Tag{"table"} << std::move(funnel_rows)
+                       << CTag{"table"};
+  LOG_AF(log_manager_) << LoggingScope::kMetrics << LogMessage::kKeyMetrics
+                       << Tag{"table"} << std::move(key_metrics_rows)
+                       << CTag{"table"};
 }
 
 void FormEventLoggerBase::RecordAblationMetrics() {
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc
index 9546585..8226766 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -1021,18 +1021,17 @@
 void CreditCardSaveManager::LogCardUploadDecisionsToAutofillInternals(
     int upload_decision_metrics) {
   LogManager* log_manager = client_->GetLogManager();
-  if (!log_manager)
-    return;
 
   auto final_decision =
       (upload_decision_metrics_ & AutofillMetrics::UPLOAD_OFFERED)
           ? LogMessage::kCardUploadDecisionUploadOffered
           : LogMessage::kCardUploadDecisionUploadNotOffered;
 
-  auto buffer = log_manager->Log();
-  buffer << LoggingScope::kCardUploadDecision << final_decision;
-  buffer << Tag{"div"} << Attrib{"class", "form"} << Tag{"tr"} << Tag{"td"}
-         << "Decision Metrics:" << CTag{"td"} << Tag{"td"} << Tag{"table"};
+  LogBuffer buffer(IsLoggingActive(log_manager));
+  LOG_AF(buffer) << LoggingScope::kCardUploadDecision << final_decision;
+  LOG_AF(buffer) << Tag{"div"} << Attrib{"class", "form"} << Tag{"tr"}
+                 << Tag{"td"} << "Decision Metrics:" << CTag{"td"} << Tag{"td"}
+                 << Tag{"table"};
 
   for (int i = 0; i < AutofillMetrics::kNumCardUploadDecisionMetrics; i++) {
     AutofillMetrics::CardUploadDecisionMetric currentBitmaskValue =
@@ -1100,9 +1099,10 @@
         result = "UPLOAD_NOT_OFFERED_INVALID_LEGAL_MESSAGE";
         break;
     }
-    buffer << Tr{} << result;
+    LOG_AF(buffer) << Tr{} << result;
   }
-  buffer << CTag{"table"} << CTag{"td"} << CTag{"tr"} << CTag{"div"};
+  LOG_AF(buffer) << CTag{"table"} << CTag{"td"} << CTag{"tr"} << CTag{"div"};
+  LOG_AF(log_manager) << std::move(buffer);
 }
 
 void CreditCardSaveManager::LogSaveCardRequestExpirationDateReasonMetric() {
diff --git a/components/autofill/core/common/BUILD.gn b/components/autofill/core/common/BUILD.gn
index 0660e3a..77b2d6d 100644
--- a/components/autofill/core/common/BUILD.gn
+++ b/components/autofill/core/common/BUILD.gn
@@ -43,6 +43,7 @@
     "language_code.h",
     "logging/log_buffer.cc",
     "logging/log_buffer.h",
+    "logging/log_macros.h",
     "password_form_fill_data.cc",
     "password_form_fill_data.h",
     "password_form_generation_data.h",
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index aa0ae53d..3227296 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -132,7 +132,7 @@
 // HideViewAndDie() function, but as a delayed task.
 // TODO(crbug.com/1277218): Cleanup when launched.
 const base::Feature kAutofillDelayPopupControllerDeletion{
-    "AutofillDelayPopupControllerDeletion", base::FEATURE_DISABLED_BY_DEFAULT};
+    "AutofillDelayPopupControllerDeletion", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Kill switch for Autofill filling.
 const base::Feature kAutofillDisableFilling{"AutofillDisableFilling",
diff --git a/components/autofill/core/common/logging/log_buffer.cc b/components/autofill/core/common/logging/log_buffer.cc
index 7f2dffb..1cae4906 100644
--- a/components/autofill/core/common/logging/log_buffer.cc
+++ b/components/autofill/core/common/logging/log_buffer.cc
@@ -82,8 +82,9 @@
 
 }  // namespace
 
-LogBuffer::LogBuffer() {
-  buffer_.push_back(CreateEmptyFragment());
+LogBuffer::LogBuffer(IsActive active) : active_(*active) {
+  if (active_)
+    buffer_.push_back(CreateEmptyFragment());
 }
 
 LogBuffer::LogBuffer(LogBuffer&& other) noexcept = default;
@@ -242,7 +243,7 @@
 template <typename T, typename CharT = typename T::value_type>
 LogBuffer HighlightValueInternal(T haystack, T needle) {
   using StringPieceT = base::BasicStringPiece<CharT>;
-  LogBuffer buffer;
+  LogBuffer buffer(LogBuffer::IsActive(true));
   size_t pos = haystack.find(needle);
   if (pos == StringPieceT::npos || needle.empty()) {
     buffer << haystack;
diff --git a/components/autofill/core/common/logging/log_buffer.h b/components/autofill/core/common/logging/log_buffer.h
index a71b156..bbf2ce2 100644
--- a/components/autofill/core/common/logging/log_buffer.h
+++ b/components/autofill/core/common/logging/log_buffer.h
@@ -12,7 +12,9 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
+#include "base/types/strong_alias.h"
 #include "base/values.h"
+#include "components/autofill/core/common/logging/log_macros.h"
 #include "url/gurl.h"
 
 // The desired pattern to generate log messages is to pass a scope, a log
@@ -46,6 +48,21 @@
 //   LogBuffer buffer;
 //   for (...) { buffer << something; }
 //   LogBuffer() << std::move(buffer);
+//
+// In practice the LogBuffer requires a boolean parameter indicating whether
+// logging should happen. You should rely on
+// components/autofill/core/common/logging/log_macros.h and follow one of the
+// following patterns:
+//
+// (1) void MyFunction(LogManager* log_manager) {
+//       LOG_AF(log_mannager) << "foobar";
+//     }
+// (2) void MyFunction(LogManager* log_manager) {
+//       LogBuffer buffer(
+//          /*active=*/ log_manager && log_manager->IsLoggingActive());
+//       LOG_AF(buffer) << "foobar";
+//       LOG_AF(log_manager) << std::move(buffer);
+//     }
 
 namespace autofill {
 
@@ -76,11 +93,15 @@
 // See LogTableRowBuffer below.
 struct Tr {};
 
+class LogManager;
+
 // A buffer into which you can stream values. See the top of this header file
 // for samples.
 class LogBuffer {
  public:
-  LogBuffer();
+  using IsActive = base::StrongAlias<struct ActiveTag, bool>;
+
+  explicit LogBuffer(IsActive active = IsActive(true));
   ~LogBuffer();
 
   LogBuffer(LogBuffer&& other) noexcept;
@@ -95,7 +116,6 @@
   // Returns whether an active WebUI is listening. If false, the buffer may
   // not do any logging.
   bool active() const { return active_; }
-  void set_active(bool active) { active_ = active; }
 
  private:
   friend LogBuffer& operator<<(LogBuffer& buf, Tag&& tag);
@@ -205,6 +225,36 @@
 LogBuffer HighlightValue(base::StringPiece16 haystack,
                          base::StringPiece16 needle);
 
+namespace internal {
+
+// Traits for LOG_AF() macro for `LogBuffer*`.
+template <typename T>
+struct LoggerTraits<
+    T,
+    typename std::enable_if_t<
+        std::is_convertible_v<decltype(std::declval<T>()), const LogBuffer*>>> {
+  static bool active(const LogBuffer* log_buffer) {
+    return log_buffer && log_buffer->active();
+  }
+
+  static LogBuffer& get_stream(LogBuffer* log_buffer) { return *log_buffer; }
+};
+
+// Traits for LOG_AF() macro for `LogBuffer&`.
+template <typename T>
+struct LoggerTraits<
+    T,
+    typename std::enable_if_t<
+        std::is_convertible_v<decltype(std::declval<T>()), const LogBuffer&>>> {
+  static bool active(const LogBuffer& log_buffer) {
+    return log_buffer.active();
+  }
+
+  static LogBuffer& get_stream(LogBuffer& log_buffer) { return log_buffer; }
+};
+
+}  // namespace internal
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_BUFFER_H_
diff --git a/components/autofill/core/common/logging/log_macros.h b/components/autofill/core/common/logging/log_macros.h
new file mode 100644
index 0000000..e7253ad
--- /dev/null
+++ b/components/autofill/core/common/logging/log_macros.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_MACROS_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_MACROS_H_
+
+// Logging macro in the style of LOG(INFO) intended for
+// chrome://autofill-internals.
+//
+// In `LOG_AF(logger) << expression`, the `expression` is evaluated only
+// if the `logger` is active. The expression `logger` must be of type
+// `LogManager` or `LogBuffer` or `LogManager*` or `LogBuffer`*.
+//
+// Support for other types of `logger` can be added by adding template
+// specializations of `LoggerTraits`.
+#define LOG_AF(logger)                                                        \
+  !::autofill::internal::LoggerTraits<decltype(logger)>::active(logger)       \
+      ? (void)0                                                               \
+      : ::autofill::internal::Voidify() &                                     \
+            ::autofill::internal::LoggerTraits<decltype(logger)>::get_stream( \
+                logger)
+
+namespace autofill::internal {
+
+// Traits for targets of LOG_AF(). There are currently specializations for
+// `LogManager*` and `LogBuffer*`.
+template <typename T, typename Enable = void>
+struct LoggerTraits {
+  // Returns true iff logging to should be enabled.
+  static bool active(const T& logger) { return false; }
+
+  // Returns an object that implements the stream insertion operator
+  // operator<<().
+  static int get_stream(const T& logger) { return {}; }
+};
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class Voidify {
+ public:
+  Voidify() = default;
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  template <typename U>
+  void operator&(const U&) {}
+};
+
+}  // namespace autofill::internal
+
+#endif  // COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_MACROS_H_
diff --git a/components/background_task_scheduler/BUILD.gn b/components/background_task_scheduler/BUILD.gn
index da8536a..bd8ebd3 100644
--- a/components/background_task_scheduler/BUILD.gn
+++ b/components/background_task_scheduler/BUILD.gn
@@ -138,7 +138,6 @@
       "//third_party/android_deps:chromium_play_services_availability_shadows_java",
       "//third_party/junit",
       "//third_party/mockito:mockito_java",
-      "//third_party/robolectric:robolectric_test_sdk_java",
     ]
   }
 }
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 0878682..ad8d500 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "10.24",
-  "log_list_timestamp": "2022-06-26T12:58:21Z",
+  "version": "10.25",
+  "log_list_timestamp": "2022-06-27T12:56:16Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/nacl/loader/nacl_ipc_adapter.cc b/components/nacl/loader/nacl_ipc_adapter.cc
index 78cdcaa..28deed64 100644
--- a/components/nacl/loader/nacl_ipc_adapter.cc
+++ b/components/nacl/loader/nacl_ipc_adapter.cc
@@ -742,8 +742,8 @@
   // Length of the message not including the body. The data passed to us by the
   // plugin should match that in the message header. This should have already
   // been validated by GetBufferStatus.
-  size_t body_len = buffer_len - sizeof(NaClMessageHeader);
-  CHECK(body_len == header->payload_size);
+  int body_len = static_cast<int>(buffer_len - sizeof(NaClMessageHeader));
+  DCHECK(body_len == static_cast<int>(header->payload_size));
 
   // We actually discard the flags and only copy the ones we care about. This
   // is just because message doesn't have a constructor that takes raw flags.
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
index 1a488a4..6387765f 100644
--- a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
+++ b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
@@ -47,8 +47,7 @@
   DCHECK(built_in_backend_);
   DCHECK(android_backend_);
   active_backend_ = std::make_unique<PasswordStoreProxyBackend>(
-      built_in_backend_.get(), android_backend_.get(), prefs_,
-      sync_delegate_.get());
+      built_in_backend_.get(), android_backend_.get(), prefs_);
 }
 
 PasswordStoreBackendMigrationDecorator::
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.cc b/components/password_manager/core/browser/password_store_proxy_backend.cc
index d529a872..d40b889 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend.cc
+++ b/components/password_manager/core/browser/password_store_proxy_backend.cc
@@ -29,6 +29,8 @@
 
 namespace {
 
+using sync_util::IsPasswordSyncEnabled;
+
 bool ShouldExecuteModifyOperationsOnShadowBackend(PrefService* prefs,
                                                   bool is_syncing) {
   // TODO(crbug.com/1306001): Reenable or clean up for local-only users.
@@ -346,12 +348,10 @@
 PasswordStoreProxyBackend::PasswordStoreProxyBackend(
     PasswordStoreBackend* built_in_backend,
     PasswordStoreBackend* android_backend,
-    PrefService* prefs,
-    SyncDelegate* sync_delegate)
+    PrefService* prefs)
     : built_in_backend_(built_in_backend),
       android_backend_(android_backend),
-      prefs_(prefs),
-      sync_delegate_(sync_delegate) {}
+      prefs_(prefs) {}
 
 PasswordStoreProxyBackend::~PasswordStoreProxyBackend() = default;
 
@@ -408,7 +408,7 @@
           .Then(std::move(callback)));
 
   if (ShouldExecuteReadOperationsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->GetAllLoginsAsync(
         base::BindOnce(&ShadowTrafficMetricsRecorder<
                            LoginsResultOrErrorImpl>::RecordShadowResult,
@@ -428,7 +428,7 @@
           .Then(std::move(callback)));
 
   if (ShouldExecuteReadOperationsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->GetAutofillableLoginsAsync(
         base::BindOnce(&ShadowTrafficMetricsRecorder<
                            LoginsResultOrErrorImpl>::RecordShadowResult,
@@ -457,7 +457,7 @@
       include_psl, forms);
 
   if (ShouldExecuteReadOperationsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->FillMatchingLoginsAsync(
         base::BindOnce(&ShadowTrafficMetricsRecorder<
                            LoginsResultOrErrorImpl>::RecordShadowResult,
@@ -484,7 +484,7 @@
                            handler)
                 .Then(std::move(maybe_retry_callback)));
   if (ShouldExecuteModifyOperationsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->AddLoginAsync(
         form,
         base::BindOnce(&ShadowTrafficMetricsRecorder<
@@ -511,7 +511,7 @@
                            handler)
                 .Then(std::move(maybe_retry_callback)));
   if (ShouldExecuteModifyOperationsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->UpdateLoginAsync(
         form,
         base::BindOnce(&ShadowTrafficMetricsRecorder<
@@ -533,7 +533,7 @@
                            handler)
                 .Then(std::move(callback)));
   if (ShouldExecuteDeletionsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->RemoveLoginAsync(
         form,
         base::BindOnce(&ShadowTrafficMetricsRecorder<
@@ -559,7 +559,7 @@
                      handler)
           .Then(std::move(callback)));
   if (ShouldExecuteDeletionsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->RemoveLoginsByURLAndTimeAsync(
         url_filter, std::move(delete_begin), std::move(delete_end),
         base::OnceCallback<void(bool)>(),
@@ -584,7 +584,7 @@
                      handler)
           .Then(std::move(callback)));
   if (ShouldExecuteDeletionsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->RemoveLoginsCreatedBetweenAsync(
         std::move(delete_begin), std::move(delete_end),
         base::BindOnce(&ShadowTrafficMetricsRecorder<
@@ -599,7 +599,7 @@
   main_backend()->DisableAutoSignInForOriginsAsync(origin_filter,
                                                    std::move(completion));
   if (ShouldExecuteModifyOperationsOnShadowBackend(
-          prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
+          prefs_, IsPasswordSyncEnabled(sync_service_))) {
     shadow_backend()->DisableAutoSignInForOriginsAsync(
         origin_filter,
         /*completion=*/base::DoNothing());
@@ -700,7 +700,7 @@
   if (prefs_->GetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors))
     return false;
 
-  if (!sync_delegate_->IsSyncingPasswordsEnabled())
+  if (!IsPasswordSyncEnabled(sync_service_))
     return false;
 
   if (!base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerAndroid))
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.h b/components/password_manager/core/browser/password_store_proxy_backend.h
index 4d9bc13..db4a820 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend.h
+++ b/components/password_manager/core/browser/password_store_proxy_backend.h
@@ -28,8 +28,7 @@
   // this object as long as Shutdown() is not called.
   PasswordStoreProxyBackend(PasswordStoreBackend* built_in_backend,
                             PasswordStoreBackend* android_backend,
-                            PrefService* prefs,
-                            SyncDelegate* sync_delegate);
+                            PrefService* prefs);
   PasswordStoreProxyBackend(const PasswordStoreProxyBackend&) = delete;
   PasswordStoreProxyBackend(PasswordStoreProxyBackend&&) = delete;
   PasswordStoreProxyBackend& operator=(const PasswordStoreProxyBackend&) =
@@ -118,7 +117,6 @@
   const raw_ptr<PasswordStoreBackend> built_in_backend_;
   const raw_ptr<PasswordStoreBackend> android_backend_;
   raw_ptr<PrefService> const prefs_ = nullptr;
-  const raw_ptr<SyncDelegate> sync_delegate_;
   raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
 
   base::WeakPtrFactory<PasswordStoreProxyBackend> weak_ptr_factory_{this};
diff --git a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
index 0ebadd0..3d80977 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
+++ b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
@@ -91,7 +91,7 @@
  protected:
   PasswordStoreProxyBackendTest() {
     proxy_backend_ = std::make_unique<PasswordStoreProxyBackend>(
-        &built_in_backend_, &android_backend_, &prefs_, &sync_delegate_);
+        &built_in_backend_, &android_backend_, &prefs_);
 
     feature_list_.InitAndEnableFeatureWithParameters(
         features::kUnifiedPasswordManagerAndroid, {{"stage", "1"}});
@@ -114,7 +114,16 @@
     proxy_backend_.reset();
   }
 
-  MockPasswordBackendSyncDelegate& sync_delegate() { return sync_delegate_; }
+  void EnablePasswordSync() {
+    sync_service_.GetUserSettings()->SetSelectedTypes(
+        /*sync_everything=*/false, {syncer::UserSelectableType::kPasswords});
+  }
+
+  void DisablePasswordSync() {
+    sync_service_.GetUserSettings()->SetSelectedTypes(
+        /*sync_everything=*/false, /*types=*/{});
+  }
+
   PasswordStoreBackend& proxy_backend() { return *proxy_backend_; }
   MockPasswordStoreBackend& built_in_backend() { return built_in_backend_; }
   MockPasswordStoreBackend& android_backend() { return android_backend_; }
@@ -188,8 +197,7 @@
                               base::DoNothing());
 
   // With sync enabled, only the android backend calls the original callback.
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
   EXPECT_CALL(original_callback, Run);
   android_remote_changes_callback.Run(absl::nullopt);
   testing::Mock::VerifyAndClearExpectations(&original_callback);
@@ -200,9 +208,7 @@
 
   // As soon as sync is disabled, only the built-in backend calls the original
   // callback. The callbacks are stable. No new Init call is necessary.
-  testing::Mock::VerifyAndClearExpectations(&sync_delegate());
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(original_callback, Run).Times(0);
   android_remote_changes_callback.Run(absl::nullopt);
@@ -230,8 +236,8 @@
                               base::DoNothing());
 
   // With sync enabled, only the built-in backend calls the original callback.
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
+
   EXPECT_CALL(original_callback, Run).Times(0);
   android_sync_callback.Run();
   testing::Mock::VerifyAndClearExpectations(&original_callback);
@@ -242,9 +248,7 @@
 
   // With sync is disabled, the built-in backend remains the only to call the
   // original callback.
-  testing::Mock::VerifyAndClearExpectations(&sync_delegate());
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(original_callback, Run).Times(0);
   android_sync_callback.Run();
@@ -263,8 +267,7 @@
       .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
         std::move(reply).Run(CreateTestLogins());
       })));
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
   EXPECT_CALL(android_backend(), GetAllLoginsAsync);
   proxy_backend().GetAllLoginsAsync(mock_reply.Get());
 }
@@ -279,8 +282,7 @@
       .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
         std::move(reply).Run(CreateTestLogins());
       })));
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
   EXPECT_CALL(android_backend(), GetAutofillableLoginsAsync);
   proxy_backend().GetAutofillableLoginsAsync(mock_reply.Get());
 }
@@ -294,8 +296,7 @@
       .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
         std::move(reply).Run(CreateTestLogins());
       })));
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
   EXPECT_CALL(android_backend(), FillMatchingLoginsAsync);
   proxy_backend().FillMatchingLoginsAsync(mock_reply.Get(),
                                           /*include_psl=*/false,
@@ -440,8 +441,7 @@
       .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
         std::move(reply).Run(CreateTestLogins());
       })));
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
   EXPECT_CALL(android_backend(), GetAllLoginsAsync).Times(0);
   proxy_backend().GetAllLoginsAsync(mock_reply.Get());
 
@@ -465,8 +465,7 @@
       features::kUnifiedPasswordManagerAndroid,
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), GetAutofillableLoginsAsync);
   EXPECT_CALL(android_backend(), GetAutofillableLoginsAsync).Times(0);
@@ -480,8 +479,7 @@
       features::kUnifiedPasswordManagerAndroid,
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), FillMatchingLoginsAsync);
   EXPECT_CALL(android_backend(), FillMatchingLoginsAsync).Times(0);
@@ -491,8 +489,7 @@
 }
 
 TEST_F(PasswordStoreProxyBackendTest, NoShadowAddLoginAsyncWhenSyncEnabled) {
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), AddLoginAsync);
   EXPECT_CALL(android_backend(), AddLoginAsync).Times(0);
@@ -508,8 +505,7 @@
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), AddLoginAsync);
   EXPECT_CALL(android_backend(), AddLoginAsync).Times(0);
@@ -518,8 +514,7 @@
 }
 
 TEST_F(PasswordStoreProxyBackendTest, NoShadowUpdateLoginAsyncWhenSyncEnabled) {
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), UpdateLoginAsync);
   EXPECT_CALL(android_backend(), UpdateLoginAsync).Times(0);
@@ -535,8 +530,7 @@
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), UpdateLoginAsync);
   EXPECT_CALL(android_backend(), UpdateLoginAsync).Times(0);
@@ -545,8 +539,7 @@
 }
 
 TEST_F(PasswordStoreProxyBackendTest, NoShadowRemoveLoginAsyncWhenSyncEnabled) {
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginAsync);
   EXPECT_CALL(android_backend(), RemoveLoginAsync).Times(0);
@@ -562,8 +555,7 @@
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginAsync);
   EXPECT_CALL(android_backend(), RemoveLoginAsync).Times(0);
@@ -578,8 +570,7 @@
       features::kUnifiedPasswordManagerAndroid,
       {{"migration_version", "1"}, {"stage", "0"}});
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginAsync);
   EXPECT_CALL(android_backend(), RemoveLoginAsync);
@@ -589,8 +580,7 @@
 
 TEST_F(PasswordStoreProxyBackendTest,
        NoShadowRemoveLoginsByURLAndTimeAsyncWhenSyncEnabled) {
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginsByURLAndTimeAsync);
   EXPECT_CALL(android_backend(), RemoveLoginsByURLAndTimeAsync).Times(0);
@@ -611,8 +601,7 @@
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginsByURLAndTimeAsync);
   EXPECT_CALL(android_backend(), RemoveLoginsByURLAndTimeAsync).Times(0);
@@ -632,8 +621,7 @@
       features::kUnifiedPasswordManagerAndroid,
       {{"migration_version", "1"}, {"stage", "0"}});
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginsByURLAndTimeAsync);
   EXPECT_CALL(android_backend(), RemoveLoginsByURLAndTimeAsync);
@@ -647,8 +635,7 @@
 
 TEST_F(PasswordStoreProxyBackendTest,
        NoShadowRemoveLoginsCreatedBetweenAsyncWhenSyncEnabled) {
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync);
   EXPECT_CALL(android_backend(), RemoveLoginsCreatedBetweenAsync).Times(0);
@@ -667,8 +654,7 @@
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync);
   EXPECT_CALL(android_backend(), RemoveLoginsCreatedBetweenAsync).Times(0);
@@ -686,8 +672,7 @@
       features::kUnifiedPasswordManagerAndroid,
       {{"migration_version", "1"}, {"stage", "0"}});
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync);
   EXPECT_CALL(android_backend(), RemoveLoginsCreatedBetweenAsync);
@@ -699,8 +684,7 @@
 
 TEST_F(PasswordStoreProxyBackendTest,
        NoShadowDisableAutoSignInForOriginsAsyncWhenSyncEnabled) {
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), DisableAutoSignInForOriginsAsync);
   EXPECT_CALL(android_backend(), DisableAutoSignInForOriginsAsync).Times(0);
@@ -717,8 +701,7 @@
       {{"migration_version", "2"}, {"stage", "1"}});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   EXPECT_CALL(built_in_backend(), DisableAutoSignInForOriginsAsync);
   EXPECT_CALL(android_backend(), DisableAutoSignInForOriginsAsync).Times(0);
@@ -741,8 +724,7 @@
       features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
 
   // Imitate password sync being disabled in settings.
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(false));
+  DisablePasswordSync();
 
   // Verify that android backend is not used.
   EXPECT_CALL(android_backend(), GetAllLoginsAsync).Times(0);
@@ -759,8 +741,7 @@
   prefs()->SetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
                       true);
 
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   // Verify that android backend is not used.
   EXPECT_CALL(android_backend(), GetAllLoginsAsync).Times(0);
@@ -774,8 +755,7 @@
   // Enable UPM for syncing users only.
   feature_list.InitAndEnableFeatureWithParameters(
       features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
 
@@ -798,8 +778,7 @@
   // Enable UPM for syncing users only.
   feature_list.InitAndEnableFeatureWithParameters(
       features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
 
@@ -821,8 +800,7 @@
   // Enable UPM for syncing users only.
   feature_list.InitAndEnableFeatureWithParameters(
       features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
-  EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-      .WillRepeatedly(Return(true));
+  EnablePasswordSync();
 
   base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
 
@@ -893,8 +871,7 @@
         .WillOnce(WithArg<0>(Invoke([&p](LoginsOrErrorReply reply) -> void {
           std::move(reply).Run(p.GetMainLogins());
         })));
-    EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-        .WillRepeatedly(Return(true));
+    EnablePasswordSync();
     EXPECT_CALL(android_backend(), GetAllLoginsAsync)
         .WillOnce(WithArg<0>(Invoke([&p](LoginsOrErrorReply reply) -> void {
           std::move(reply).Run(p.GetShadowLogins());
@@ -1011,15 +988,11 @@
         features::kUnifiedPasswordManagerAndroid,
         {{"stage",
           base::NumberToString(static_cast<int>(GetParam().variation))}});
-    EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
-        .WillRepeatedly(Return(GetParam().is_sync_enabled));
 
     if (GetParam().is_sync_enabled) {
-      sync_service()->GetUserSettings()->SetSelectedTypes(
-          /*sync_everything=*/false, {syncer::UserSelectableType::kPasswords});
+      EnablePasswordSync();
     } else {
-      sync_service()->GetUserSettings()->SetSelectedTypes(
-          /*sync_everything=*/false, {});
+      DisablePasswordSync();
     }
   }
 
diff --git a/components/resources/ssl/ssl_error_assistant/PRESUBMIT.py b/components/resources/ssl/ssl_error_assistant/PRESUBMIT.py
index 93b738b..33323a5 100644
--- a/components/resources/ssl/ssl_error_assistant/PRESUBMIT.py
+++ b/components/resources/ssl/ssl_error_assistant/PRESUBMIT.py
@@ -12,6 +12,10 @@
 # TODO(meacer): Refactor and reuse shared code with
 #               chrome/browser/resources/safe_browsing/PRESUBMIT.py
 def CheckVersionUpdatedInSSLErrorAssistantProto(input_api, output_api):
+  # Don't report errors for "git cl presubmit --all/--files"
+  if input_api.no_diffs:
+    return []
+
   def IsSSLErrorAssistantProto(x):
     return (input_api.os_path.basename(x.LocalPath()) ==
             'ssl_error_assistant.asciipb')
diff --git a/components/safe_browsing/content/resources/PRESUBMIT.py b/components/safe_browsing/content/resources/PRESUBMIT.py
index 6fe451c..18d5def 100644
--- a/components/safe_browsing/content/resources/PRESUBMIT.py
+++ b/components/safe_browsing/content/resources/PRESUBMIT.py
@@ -8,6 +8,9 @@
 USE_PYTHON3 = True
 
 def CheckVersionUpdatedInDownloadFileTypeList(input_api, output_api):
+    # Don't report errors for "git cl presubmit --all/--files"
+    if input_api.no_diffs:
+        return []
 
     download_file_type_names = [
         'download_file_types.asciipb', 'download_file_types_experiment.asciipb'
diff --git a/components/safe_search_api/url_checker.cc b/components/safe_search_api/url_checker.cc
index d312a80c..e120692 100644
--- a/components/safe_search_api/url_checker.cc
+++ b/components/safe_search_api/url_checker.cc
@@ -18,7 +18,6 @@
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "components/google/core/common/google_util.h"
 
 namespace safe_search_api {
 
@@ -29,10 +28,6 @@
 
 }  // namespace
 
-// Consider all URLs within a google domain to be safe.
-const base::Feature kAllowAllGoogleUrls{"SafeSearchAllowAllGoogleURLs",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
 struct URLChecker::Check {
   Check(const GURL& url, CheckCallback callback);
   ~Check();
@@ -65,23 +60,6 @@
 URLChecker::~URLChecker() = default;
 
 bool URLChecker::CheckURL(const GURL& url, CheckCallback callback) {
-  if (base::FeatureList::IsEnabled(kAllowAllGoogleUrls)) {
-    // Hack: For now, allow all Google URLs to save QPS.
-    if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
-                                       google_util::ALLOW_NON_STANDARD_PORTS)) {
-      std::move(callback).Run(url, Classification::SAFE, false);
-      return true;
-    }
-    // Hack: For now, allow all YouTube URLs since YouTube has its own Safety
-    // Mode anyway.
-    if (google_util::IsYoutubeDomainUrl(
-            url, google_util::ALLOW_SUBDOMAIN,
-            google_util::ALLOW_NON_STANDARD_PORTS)) {
-      std::move(callback).Run(url, Classification::SAFE, false);
-      return true;
-    }
-  }
-
   auto cache_it = cache_.Get(url);
   if (cache_it != cache_.end()) {
     const CheckResult& result = cache_it->second;
diff --git a/components/safe_search_api/url_checker.h b/components/safe_search_api/url_checker.h
index f164001..a23027b 100644
--- a/components/safe_search_api/url_checker.h
+++ b/components/safe_search_api/url_checker.h
@@ -15,18 +15,11 @@
 #include "components/safe_search_api/url_checker_client.h"
 #include "url/gurl.h"
 
-namespace base {
-struct Feature;
-}
-
 namespace safe_search_api {
 
 // The SafeSearch API classification of a URL.
 enum class Classification { SAFE, UNSAFE };
 
-// Visible for testing.
-extern const base::Feature kAllowAllGoogleUrls;
-
 // This class uses one implementation of URLCheckerClient to check the
 // classification of the content on a given URL and returns the result
 // asynchronously via a callback. It is also responsible for the synchronous
diff --git a/components/safe_search_api/url_checker_unittest.cc b/components/safe_search_api/url_checker_unittest.cc
index 6b0a99fa..dbc308b 100644
--- a/components/safe_search_api/url_checker_unittest.cc
+++ b/components/safe_search_api/url_checker_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/safe_search_api/fake_url_checker_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -162,40 +161,6 @@
   ASSERT_FALSE(SendResponse(url, Classification::UNSAFE, false));
 }
 
-TEST_F(SafeSearchURLCheckerTest, AllowAllGoogleURLs) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(kAllowAllGoogleUrls);
-  {
-    GURL url("https://sites.google.com/porn");
-    EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, _));
-    // No server interaction.
-    bool cache_hit = CheckURL(url);
-    ASSERT_TRUE(cache_hit);
-  }
-  {
-    GURL url("https://youtube.com/porn");
-    EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, _));
-    // No server interaction
-    bool cache_hit = CheckURL(url);
-    ASSERT_TRUE(cache_hit);
-  }
-}
-
-TEST_F(SafeSearchURLCheckerTest, NoAllowAllGoogleURLs) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndDisableFeature(kAllowAllGoogleUrls);
-  {
-    GURL url("https://sites.google.com/porn");
-    EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, false));
-    ASSERT_FALSE(SendResponse(url, Classification::UNSAFE, false));
-  }
-  {
-    GURL url("https://youtube.com/porn");
-    EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, false));
-    ASSERT_FALSE(SendResponse(url, Classification::UNSAFE, false));
-  }
-}
-
 TEST_F(SafeSearchURLCheckerTest, DestroyURLCheckerBeforeCallback) {
   GURL url(GetNewURL());
   EXPECT_CALL(*this, OnCheckDone(_, _, _)).Times(0);
diff --git a/components/strings/components_strings_da.xtb b/components/strings/components_strings_da.xtb
index 0dac414..7afe463 100644
--- a/components/strings/components_strings_da.xtb
+++ b/components/strings/components_strings_da.xtb
@@ -936,6 +936,7 @@
 <translation id="3477679029130949506">Filmoversigter og spilletider</translation>
 <translation id="3479552764303398839">Ikke nu</translation>
 <translation id="3484560055331845446">Du kan miste adgangen til din Google-konto. Chrome anbefaler, at du skifter din adgangskode nu. Du bliver bedt om at logge ind.</translation>
+<translation id="3484861421501147767">Påmindelse: En gemt kampagnekode er tilgængelig</translation>
 <translation id="3487845404393360112">Bakke 4</translation>
 <translation id="3495081129428749620">Find på side
     <ph name="PAGE_TITLE" /></translation>
@@ -2224,6 +2225,7 @@
             <ph name="LIST_ITEM" />Oplysninger, der er angivet i formularer<ph name="END_LIST_ITEM" />
           <ph name="END_LIST" /></translation>
 <translation id="7129409597930077180">Der kan ikke sendes til denne adresse. Vælg en anden adresse.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
 <translation id="7132939140423847331">Din administrator tillader ikke kopiering af disse data.</translation>
 <translation id="7135130955892390533">Vis status</translation>
 <translation id="7138472120740807366">Leveringsmetode</translation>
diff --git a/components/strings/components_strings_mn.xtb b/components/strings/components_strings_mn.xtb
index 5bde2f94..91601dc 100644
--- a/components/strings/components_strings_mn.xtb
+++ b/components/strings/components_strings_mn.xtb
@@ -937,6 +937,7 @@
 <translation id="3477679029130949506">Киноны жагсаалтын театрын үзвэрийн цаг</translation>
 <translation id="3479552764303398839">Одоо биш</translation>
 <translation id="3484560055331845446">Та Google Бүртгэлийнхээ хандалтыг алдаж болзошгүй. Chrome таныг нууц үгээ одоо солихыг зөвлөж байна. Танаас нэвтрэхийг асууна.</translation>
+<translation id="3484861421501147767">Сануулагч: Хадгалсан урамшууллын код боломжтой</translation>
 <translation id="3487845404393360112">Гарах цаасны тавиур 4</translation>
 <translation id="3495081129428749620">Хуудаснаас олох
     <ph name="PAGE_TITLE" /></translation>
@@ -2224,6 +2225,7 @@
             <ph name="LIST_ITEM" />Маягтад оруулсан мэдээлэл<ph name="END_LIST_ITEM" />
           <ph name="END_LIST" /></translation>
 <translation id="7129409597930077180">Энэ хаяг руу хүргэх боломжгүй тул өөр хаяг сонгоно уу.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
 <translation id="7132939140423847331">Таны админ энэ өгөгдлийг хуулахыг хориглосон.</translation>
 <translation id="7135130955892390533">Төлөвийг харуулах</translation>
 <translation id="7138472120740807366">Хүргэлтийн арга</translation>
diff --git a/components/strings/components_strings_uz.xtb b/components/strings/components_strings_uz.xtb
index 9b70267..549d33a 100644
--- a/components/strings/components_strings_uz.xtb
+++ b/components/strings/components_strings_uz.xtb
@@ -933,6 +933,7 @@
 <translation id="3477679029130949506">Kinoteatrdagi filmlar va namoyish vaqtlari</translation>
 <translation id="3479552764303398839">Hozir emas</translation>
 <translation id="3484560055331845446">Google hisobingiz xavf ostida. Parolingizni yangilashni tavsiya qilamiz. Hisobingizga qayta kirishingiz talab qilinadi.</translation>
+<translation id="3484861421501147767">Eslatma: Saqlangan promo kod mavjud</translation>
 <translation id="3487845404393360112">Tarnov 4</translation>
 <translation id="3495081129428749620">“<ph name="PAGE_TITLE" />” sahifasidan qidirish</translation>
 <translation id="350069200438440499">Fayl nomi:</translation>
@@ -2218,6 +2219,7 @@
             <ph name="LIST_ITEM" />Shakllarga kiritilgan maʼlumotlar<ph name="END_LIST_ITEM" />
           <ph name="END_LIST" /></translation>
 <translation id="7129409597930077180">Bu manzilga yetkazib bera olmaymiz. Boshqa manzilni tanlang.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
 <translation id="7132939140423847331">Administrator bu maʼlumotlardan nusxa olishni taqiqlagan.</translation>
 <translation id="7135130955892390533">Holat axboroti</translation>
 <translation id="7138472120740807366">Yetkazib berish usuli</translation>
diff --git a/components/translate/content/android/BUILD.gn b/components/translate/content/android/BUILD.gn
index ed3843c..0d36dca 100644
--- a/components/translate/content/android/BUILD.gn
+++ b/components/translate/content/android/BUILD.gn
@@ -95,11 +95,7 @@
   srcjar_deps = [ ":translate_android_enums" ]
 }
 
-java_library("junit") {
-  # Skip platform checks since Robolectric depends on requires_android targets.
-  bypass_platform_checks = true
-  testonly = true
-
+robolectric_library("junit") {
   sources =
       [ "java/src/org/chromium/components/translate/TranslateOptionsTest.java" ]
   deps = [
diff --git a/components/viz/OWNERS b/components/viz/OWNERS
index a2277cf..e20c325 100644
--- a/components/viz/OWNERS
+++ b/components/viz/OWNERS
@@ -20,19 +20,16 @@
 
 # display_embedder / ozone
 rjkroege@chromium.org
-sadrul@chromium.org
 
 # frame sinks / surfaces
 kylechar@chromium.org
 jonross@chromium.org
 
 # gpu
-sadrul@chromium.org
 kylechar@chromium.org
 
 # hit testing
 rjkroege@chromium.org
-sadrul@chromium.org
 
 # math / geometry
 dbaron@chromium.org
@@ -46,7 +43,6 @@
 
 # viz debugger
 petermcneeley@chromium.org
-sadrul@chromium.org
 
 # scheduling / begin frames
 sunnyps@chromium.org
diff --git a/components/viz/common/quads/shared_quad_state.cc b/components/viz/common/quads/shared_quad_state.cc
index b2d8949..0ac44fd2 100644
--- a/components/viz/common/quads/shared_quad_state.cc
+++ b/components/viz/common/quads/shared_quad_state.cc
@@ -55,7 +55,8 @@
   }
   if (mask_filter_info.HasGradientMask()) {
     cc::MathUtil::AddToTracedValue("mask_filter_gradient_mask",
-                                   mask_filter_info.gradient_mask(), value);
+                                   mask_filter_info.gradient_mask().value(),
+                                   value);
   }
 
   if (clip_rect) {
diff --git a/components/viz/common/resources/resource_format_utils.h b/components/viz/common/resources/resource_format_utils.h
index ae1328b9..8d73bfdb 100644
--- a/components/viz/common/resources/resource_format_utils.h
+++ b/components/viz/common/resources/resource_format_utils.h
@@ -52,8 +52,7 @@
     ResourceFormat format,
     bool use_angle_rgbx_format);
 
-// Returns whether the format can be used with GpuMemoryBuffer texture storage,
-// allocated through TexStorage2DImageCHROMIUM.
+// Returns whether the format can be used with GpuMemoryBuffer texture storage.
 VIZ_RESOURCE_FORMAT_EXPORT bool IsGpuMemoryBufferFormatSupported(
     ResourceFormat format);
 
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index 6c2ec90..d8698b4 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -20,6 +20,7 @@
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/media_start_stop_observer.h"
+#include "content/public/test/test_utils.h"
 #include "content/public/test/web_transport_simple_test_server.h"
 #include "content/shell/browser/shell.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
@@ -3117,188 +3118,137 @@
                     {}, {reason}, {}, FROM_HERE);
 }
 
+enum class SerialContext {
+  kDocument,
+  kWorker,
+  kNestedWorker,
+};
+
+enum class SerialType {
+  kSerial,
+  kWebUsb,
+};
+
+class BackForwardCacheBrowserWebUsbTest
+    : public BackForwardCacheBrowserTest,
+      public ::testing::WithParamInterface<
+          std::tuple<SerialContext, SerialType>> {
+ public:
+  std::string GetJsToUseSerial(SerialContext context, SerialType serial_type) {
+    switch (serial_type) {
+      case SerialType::kSerial:
+        switch (context) {
+          case SerialContext::kDocument:
+            return R"(
+              new Promise(async resolve => {
+                let ports = await navigator.serial.getPorts();
+                resolve("Found " + ports.length + " ports");
+              });
+            )";
+          case SerialContext::kWorker:
+            return R"(
+              new Promise(async resolve => {
+                const worker = new Worker(
+                    "/back_forward_cache/serial/worker.js");
+                worker.onmessage = message => resolve(message.data);
+                worker.postMessage("Run");
+              });
+            )";
+          case SerialContext::kNestedWorker:
+            return R"(
+              new Promise(async resolve => {
+                const worker = new Worker(
+                  "/back_forward_cache/serial/nested-worker.js");
+                worker.onmessage = message => resolve(message.data);
+                worker.postMessage("Run");
+              });
+            )";
+        }
+      case SerialType::kWebUsb:
+        switch (context) {
+          case SerialContext::kDocument:
+            return R"(
+              new Promise(async resolve => {
+                let devices = await navigator.usb.getDevices();
+                resolve("Found " + devices.length + " devices");
+              });
+            )";
+          case SerialContext::kWorker:
+            return R"(
+              new Promise(async resolve => {
+                const worker = new Worker(
+                    "/back_forward_cache/webusb/worker.js");
+                worker.onmessage = message => resolve(message.data);
+                worker.postMessage("Run");
+              });
+            )";
+          case SerialContext::kNestedWorker:
+            return R"(
+              new Promise(async resolve => {
+                const worker = new Worker(
+                  "/back_forward_cache/webusb/nested-worker.js");
+                worker.onmessage = message => resolve(message.data);
+                worker.postMessage("Run");
+              });
+            )";
+        }
+    }
+  }
+};
+
 // Check the BackForwardCache is disabled when the WebUSB feature is used.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, WebUSB) {
+// TODO(https://crbug.com/1339720): Consider testing in a subframe. This will
+// require adjustments to Permissions Policy.
+IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserWebUsbTest, Serials) {
   // WebUSB requires HTTPS.
   ASSERT_TRUE(CreateHttpsServer()->Start());
 
-  auto web_usb_reason = BackForwardCacheDisable::DisabledReason(
-      BackForwardCacheDisable::DisabledReasonId::kWebUSB);
+  SerialContext context;
+  SerialType serial_type;
+  std::tie(context, serial_type) = GetParam();
 
-  // Main document.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("a.test", "/title1.html"));
+  content::BackForwardCacheDisabledTester tester;
+  GURL url(https_server()->GetURL(
+      "a.test", "/cross_site_iframe_factory.html?a.test(a.test)"));
 
-    EXPECT_TRUE(NavigateToURL(shell(), url));
+  ASSERT_TRUE(NavigateToURL(shell(), url));
 
-    EXPECT_FALSE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 devices", content::EvalJs(current_frame_host(), R"(
-        new Promise(async resolve => {
-          let devices = await navigator.usb.getDevices();
-          resolve("Found " + devices.length + " devices");
-        });
-    )"));
-    EXPECT_TRUE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        current_frame_host()->GetProcess()->GetID(),
-        current_frame_host()->GetRoutingID(), web_usb_reason));
-  }
+  // Check that the frames we care about are cacheable.
+  RenderFrameHostImplWrapper main_rfh(current_frame_host());
+  RenderFrameHostImplWrapper sub_rfh(
+      current_frame_host()->child_at(0)->current_frame_host());
+  ASSERT_FALSE(main_rfh->IsBackForwardCacheDisabled());
+  ASSERT_FALSE(sub_rfh->IsBackForwardCacheDisabled());
 
-  // Nested document.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("c.com",
-                                    "/cross_site_iframe_factory.html?c(d)"));
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-    RenderFrameHostImpl* rfh_c = current_frame_host();
-    RenderFrameHostImpl* rfh_d = rfh_c->child_at(0)->current_frame_host();
+  // Execute script to use WebUSB.
+  ASSERT_EQ(
+      serial_type == SerialType::kSerial ? "Found 0 ports" : "Found 0 devices",
+      content::EvalJs(main_rfh.get(), GetJsToUseSerial(context, serial_type)));
 
-    EXPECT_FALSE(rfh_c->IsBackForwardCacheDisabled());
-    EXPECT_FALSE(rfh_d->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 devices", content::EvalJs(rfh_c, R"(
-        new Promise(async resolve => {
-          let devices = await navigator.usb.getDevices();
-          resolve("Found " + devices.length + " devices");
-        });
-    )"));
-    EXPECT_TRUE(rfh_c->IsBackForwardCacheDisabled());
-    EXPECT_FALSE(rfh_d->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        rfh_c->GetProcess()->GetID(), rfh_c->GetRoutingID(), web_usb_reason));
-  }
-
-  // Worker.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("e.test", "/title1.html"));
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-    EXPECT_FALSE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 devices", content::EvalJs(current_frame_host(), R"(
-        new Promise(async resolve => {
-          const worker = new Worker("/back_forward_cache/webusb/worker.js");
-          worker.onmessage = message => resolve(message.data);
-          worker.postMessage("Run");
-        });
-    )"));
-    EXPECT_TRUE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        current_frame_host()->GetProcess()->GetID(),
-        current_frame_host()->GetRoutingID(), web_usb_reason));
-  }
-
-  // Nested worker.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("f.test", "/title1.html"));
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-    EXPECT_FALSE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 devices", content::EvalJs(current_frame_host(), R"(
-        new Promise(async resolve => {
-          const worker = new Worker(
-            "/back_forward_cache/webusb/nested-worker.js");
-          worker.onmessage = message => resolve(message.data);
-          worker.postMessage("Run");
-        });
-    )"));
-    EXPECT_TRUE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        current_frame_host()->GetProcess()->GetID(),
-        current_frame_host()->GetRoutingID(), web_usb_reason));
-  }
+  // Verify that the correct frames are now uncacheable.
+  EXPECT_TRUE(main_rfh->IsBackForwardCacheDisabled());
+  EXPECT_FALSE(sub_rfh->IsBackForwardCacheDisabled());
+  auto expected_reason =
+      serial_type == SerialType::kSerial
+          ? BackForwardCacheDisable::DisabledReasonId::kSerial
+          : BackForwardCacheDisable::DisabledReasonId::kWebUSB;
+  EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
+      main_rfh->GetProcess()->GetID(), main_rfh->GetRoutingID(),
+      BackForwardCacheDisable::DisabledReason(expected_reason)));
 }
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    BackForwardCacheBrowserWebUsbTest,
+    testing::Combine(testing::Values(SerialContext::kDocument,
+                                     SerialContext::kWorker,
+                                     SerialContext::kNestedWorker),
+                     testing::Values(SerialType::kWebUsb
 #if !BUILDFLAG(IS_ANDROID)
-// Check that the back-forward cache is disabled when the Serial API is used.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, Serial) {
-  // Serial API requires HTTPS.
-  ASSERT_TRUE(CreateHttpsServer()->Start());
-
-  auto serial_reason = BackForwardCacheDisable::DisabledReason(
-      BackForwardCacheDisable::DisabledReasonId::kSerial);
-  // Main document.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("a.com", "/title1.html"));
-
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-
-    EXPECT_FALSE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 ports", content::EvalJs(current_frame_host(), R"(
-        new Promise(async resolve => {
-          let ports = await navigator.serial.getPorts();
-          resolve("Found " + ports.length + " ports");
-        });
-    )"));
-    EXPECT_TRUE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        current_frame_host()->GetProcess()->GetID(),
-        current_frame_host()->GetRoutingID(), serial_reason));
-  }
-
-  // Nested document.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("c.com",
-                                    "/cross_site_iframe_factory.html?c(d)"));
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-    RenderFrameHostImpl* rfh_c = current_frame_host();
-    RenderFrameHostImpl* rfh_d = rfh_c->child_at(0)->current_frame_host();
-
-    EXPECT_FALSE(rfh_c->IsBackForwardCacheDisabled());
-    EXPECT_FALSE(rfh_d->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 ports", content::EvalJs(rfh_c, R"(
-        new Promise(async resolve => {
-          let ports = await navigator.serial.getPorts();
-          resolve("Found " + ports.length + " ports");
-        });
-    )"));
-    EXPECT_TRUE(rfh_c->IsBackForwardCacheDisabled());
-    EXPECT_FALSE(rfh_d->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        rfh_c->GetProcess()->GetID(), rfh_c->GetRoutingID(), serial_reason));
-  }
-
-  // Worker.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("e.com", "/title1.html"));
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-    EXPECT_FALSE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 ports", content::EvalJs(current_frame_host(), R"(
-        new Promise(async resolve => {
-          const worker = new Worker("/back_forward_cache/serial/worker.js");
-          worker.onmessage = message => resolve(message.data);
-          worker.postMessage("Run");
-        });
-    )"));
-    EXPECT_TRUE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        current_frame_host()->GetProcess()->GetID(),
-        current_frame_host()->GetRoutingID(), serial_reason));
-  }
-
-  // Nested worker.
-  {
-    content::BackForwardCacheDisabledTester tester;
-    GURL url(https_server()->GetURL("f.com", "/title1.html"));
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-    EXPECT_FALSE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_EQ("Found 0 ports", content::EvalJs(current_frame_host(), R"(
-        new Promise(async resolve => {
-          const worker = new Worker(
-            "/back_forward_cache/serial/nested-worker.js");
-          worker.onmessage = message => resolve(message.data);
-          worker.postMessage("Run");
-        });
-    )"));
-    EXPECT_TRUE(current_frame_host()->IsBackForwardCacheDisabled());
-    EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
-        current_frame_host()->GetProcess()->GetID(),
-        current_frame_host()->GetRoutingID(), serial_reason));
-  }
-}
-#endif
+                                     ,
+                                     SerialType::kSerial
+#endif  // !BUILDFLAG(IS_ANDROID)
+                                     )));
 
 // Check that an audio suspends when the page goes to the cache and can resume
 // after restored.
diff --git a/content/browser/devtools/frame_auto_attacher.cc b/content/browser/devtools/frame_auto_attacher.cc
index 6852790..39d7e83 100644
--- a/content/browser/devtools/frame_auto_attacher.cc
+++ b/content/browser/devtools/frame_auto_attacher.cc
@@ -179,9 +179,7 @@
 
           FrameTreeNode* frame_tree_node = rfhi->frame_tree_node();
           if (frame_tree_node->IsMainFrame() &&
-              (frame_tree_node->IsFencedFrameRoot() ||
-               WebContentsImpl::FromFrameTreeNode(frame_tree_node)
-                   ->IsPortal())) {
+              WebContentsImpl::FromFrameTreeNode(frame_tree_node)->IsPortal()) {
             scoped_refptr<DevToolsAgentHost> new_host =
                 RenderFrameDevToolsAgentHost::GetOrCreateFor(frame_tree_node);
             new_hosts->insert(new_host);
@@ -294,26 +292,29 @@
   Hosts new_hosts;
   DevToolsAgentHost::List new_worklet_hosts;
   if (render_frame_host_) {
-    base::queue<FrameTreeNode*> queue;
-    for (size_t i = 0; i < render_frame_host_->child_count(); ++i) {
-      queue.push(render_frame_host_->child_at(i));
-    }
-    while (!queue.empty()) {
-      FrameTreeNode* node = queue.front();
-      queue.pop();
-      bool should_create = node->current_frame_host()->is_local_root_subframe();
-      if (should_create) {
-        scoped_refptr<DevToolsAgentHost> new_host =
-            RenderFrameDevToolsAgentHost::GetOrCreateFor(node);
-        new_hosts.insert(new_host);
-        // Note: We don't add children of a local root to |queue|, as they
-        // will be looked at by a separate TargetAutoAttacher created for the
-        // local root.
-      } else {
-        for (size_t i = 0; i < node->child_count(); ++i)
-          queue.push(node->child_at(i));
-      }
-    }
+    render_frame_host_->ForEachRenderFrameHost(base::BindRepeating(
+        [](Hosts* new_hosts, RenderFrameHostImpl* root, RenderFrameHost* rfh) {
+          auto* rfh_impl = static_cast<RenderFrameHostImpl*>(rfh);
+          if (rfh_impl == root || !rfh_impl->is_local_root())
+            return RenderFrameHost::FrameIterationAction::kContinue;
+
+          // At this point, |rfh_impl| is a local root that is in the subtree of
+          // |root|.
+          FrameTreeNode* node = rfh_impl->frame_tree_node();
+          bool should_create =
+              !node->IsMainFrame() || node->IsFencedFrameRoot();
+          if (should_create) {
+            scoped_refptr<DevToolsAgentHost> new_host =
+                RenderFrameDevToolsAgentHost::GetOrCreateFor(node);
+            new_hosts->insert(new_host);
+          }
+
+          // Note: We don't search through children of local roots as they will
+          // be handled by a FrameAutoAttacher that is created for the local
+          // root.
+          return RenderFrameHost::FrameIterationAction::kSkipChildren;
+        },
+        &new_hosts, render_frame_host_));
 
     AuctionWorkletDevToolsAgentHostManager::GetInstance().GetAllForFrame(
         render_frame_host_, &new_worklet_hosts);
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 546a6fb..fee664e 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -717,7 +717,7 @@
   if (IsChildFrame())
     return kTypeFrame;
   if (frame_tree_node_ && frame_tree_node_->IsFencedFrameRoot())
-    return kTypePage;
+    return kTypeFrame;
   if (web_contents() &&
       static_cast<WebContentsImpl*>(web_contents())->IsPortal()) {
     return kTypePage;
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.cc b/content/browser/renderer_host/agent_scheduling_group_host.cc
index 4a1ad43..bb633c5 100644
--- a/content/browser/renderer_host/agent_scheduling_group_host.cc
+++ b/content/browser/renderer_host/agent_scheduling_group_host.cc
@@ -344,16 +344,16 @@
     int32_t routing_id,
     const absl::optional<blink::FrameToken>& opener_frame_token,
     int32_t view_routing_id,
-    int32_t parent_routing_id,
+    const absl::optional<blink::RemoteFrameToken>& parent_frame_token,
     blink::mojom::TreeScopeType tree_scope_type,
     blink::mojom::FrameReplicationStatePtr replicated_state,
     const base::UnguessableToken& devtools_frame_token,
     mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
   DCHECK_EQ(state_, LifecycleState::kBound);
   mojo_remote_.get()->CreateFrameProxy(
-      token, routing_id, opener_frame_token, view_routing_id, parent_routing_id,
-      tree_scope_type, std::move(replicated_state), devtools_frame_token,
-      std::move(remote_main_frame_interfaces));
+      token, routing_id, opener_frame_token, view_routing_id,
+      parent_frame_token, tree_scope_type, std::move(replicated_state),
+      devtools_frame_token, std::move(remote_main_frame_interfaces));
 }
 
 void AgentSchedulingGroupHost::CreateSharedStorageWorkletService(
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.h b/content/browser/renderer_host/agent_scheduling_group_host.h
index 191ca0a..432c7464 100644
--- a/content/browser/renderer_host/agent_scheduling_group_host.h
+++ b/content/browser/renderer_host/agent_scheduling_group_host.h
@@ -109,7 +109,7 @@
       int32_t routing_id,
       const absl::optional<blink::FrameToken>& opener_frame_token,
       int32_t view_routing_id,
-      int32_t parent_routing_id,
+      const absl::optional<blink::RemoteFrameToken>& parent_frame_token,
       blink::mojom::TreeScopeType tree_scope_type,
       blink::mojom::FrameReplicationStatePtr replicated_state,
       const base::UnguessableToken& devtools_frame_token,
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.cc b/content/browser/renderer_host/back_forward_cache_metrics.cc
index 61e20ef0..a84ff35c 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.cc
+++ b/content/browser/renderer_host/back_forward_cache_metrics.cc
@@ -175,7 +175,7 @@
                     page_store_result_->browsing_instance_swap_result().value())
               : -1);
       SCOPED_CRASH_KEY_NUMBER(
-          "BFCacheMismatch", "not_restored",
+          "BFCacheMismatch", "blocklisted",
           page_store_result_->blocklisted_features().ToEnumBitmask());
       SCOPED_CRASH_KEY_NUMBER("BFCacheMismatch", "disabled",
                               page_store_result_->disabled_reasons().size());
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 6ce5aac..3c7b1d60 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -229,7 +229,7 @@
   if (!GetProcess()->IsInitializedAndNotDead())
     return false;
 
-  int parent_routing_id = MSG_ROUTING_NONE;
+  absl::optional<blink::RemoteFrameToken> parent_frame_token;
   if (frame_tree_node_->parent()) {
     // It is safe to use GetRenderFrameProxyHost to get the parent proxy, since
     // new child frames always start out as local frames, so a new proxy should
@@ -247,8 +247,7 @@
     if (!parent_proxy->is_render_frame_proxy_live())
       return false;
 
-    parent_routing_id = parent_proxy->GetRoutingID();
-    CHECK_NE(parent_routing_id, MSG_ROUTING_NONE);
+    parent_frame_token = parent_proxy->GetFrameToken();
   }
 
   absl::optional<blink::FrameToken> opener_frame_token;
@@ -261,7 +260,7 @@
   int view_routing_id = GetRenderViewHost()->GetRoutingID();
   GetAgentSchedulingGroup().CreateFrameProxy(
       frame_token_, routing_id_, opener_frame_token, view_routing_id,
-      parent_routing_id, frame_tree_node_->tree_scope_type(),
+      parent_frame_token, frame_tree_node_->tree_scope_type(),
       frame_tree_node_->current_replication_state().Clone(),
       frame_tree_node_->devtools_frame_token(),
       CreateAndBindRemoteMainFrameInterfaces());
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 710079d..17c5ed84 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -795,7 +795,7 @@
   const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) {
     base::PickleIterator iter(message);
     const char* data;
-    size_t data_length;
+    int data_length;
     if (!iter.ReadData(&data, &data_length))
       return nullptr;
     return reinterpret_cast<const WebInputEvent*>(data);
diff --git a/content/browser/service_worker/service_worker_internals_ui_browsertest.cc b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
index 96f5706..3d9c45a 100644
--- a/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
+++ b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
@@ -576,8 +576,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ServiceWorkerInternalsUIBrowserTest,
-                       // TODO(crbug.com/1324856): Re-enable this test
-                       DISABLED_StopStartSWReflectedOnInternalUI) {
+                       StopStartSWReflectedOnInternalUI) {
   Shell* sw_internal_ui_window = CreateNewWindow();
   NavigateToServiceWorkerInternalUI();
 
@@ -612,9 +611,11 @@
   ASSERT_EQ(kTitle2, title_watcher2.WaitAndGetTitle());
 
   // Tests that a starting service worker is reflected on internal UI.
-  const std::u16string kTitle3 = u"SW running_status: RUNNING";
+  const std::u16string kTitle3 = u"SW running_status: STARTING";
   TitleWatcher title_watcher_3(web_contents(), kTitle3);
-  SetMutationObserver("running_status", "RUNNING", kTitle3);
+  // To avoid premature timeouts and flakiness, the expected `running_status` to
+  // be asserted will be `STARTING` instead of `RUNNING`.
+  SetMutationObserver("running_status", "STARTING", kTitle3);
 
   wrapper()->StartActiveServiceWorker(GetAllRegistrations().front().scope,
                                       GetAllRegistrations().front().key,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index a409a83d..6bea609 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -5113,8 +5113,8 @@
   int view_routing_id = root->frame_tree()
                             ->GetRenderViewHost(site_instance_group_a)
                             ->GetRoutingID();
-  int parent_routing_id =
-      root->child_at(0)->render_manager()->GetProxyToParent()->GetRoutingID();
+  blink::RemoteFrameToken parent_frame_token =
+      root->child_at(0)->render_manager()->GetProxyToParent()->GetFrameToken();
 
   // Tell main frame A to delete its subframe B.
   FrameDeletedObserver observer(root->child_at(0)->current_frame_host());
@@ -5131,11 +5131,11 @@
   remote_main_frame_interfaces->main_frame_host = main_frame_host.Unbind();
 
   // Send the message to create a proxy for B's new child frame in A.  This
-  // used to crash, as parent_routing_id refers to a proxy that doesn't exist
+  // used to crash, as `parent_frame_token` refers to a proxy that doesn't exist
   // anymore.
   agent_scheduling_group_a->CreateFrameProxy(
       blink::RemoteFrameToken(), new_routing_id, absl::nullopt, view_routing_id,
-      parent_routing_id, blink::mojom::TreeScopeType::kDocument,
+      parent_frame_token, blink::mojom::TreeScopeType::kDocument,
       blink::mojom::FrameReplicationState::New(),
       base::UnguessableToken::Create(),
       std::move(remote_main_frame_interfaces));
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 2aec7fc..032e3ca 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -5842,8 +5842,8 @@
 // Test that clicking a select element in an out-of-process iframe creates
 // a popup menu in the correct position.
 IN_PROC_BROWSER_TEST_F(SitePerProcessHitTestBrowserTest, MAYBE_PopupMenuTest) {
-  GURL main_url(
-      embedded_test_server()->GetURL("/cross_site_iframe_factory.html?a(a)"));
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
   FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
diff --git a/content/browser/webid/webid_browsertest.cc b/content/browser/webid/webid_browsertest.cc
index fb7d93a8..dd6683d 100644
--- a/content/browser/webid/webid_browsertest.cc
+++ b/content/browser/webid/webid_browsertest.cc
@@ -194,10 +194,11 @@
                 url: ')" +
            BaseIdpUrl() + R"(',
                 clientId: 'client_id_1',
+                nonce: '12345',
               }]
             }
           }));
-          return (await x.login({nonce: '12345'})).idToken;
+          return x.idToken;
         }) ()
     )";
   }
@@ -262,10 +263,11 @@
                 url: 'http://idp.example)" +
                        base::NumberToString(https_server().port()) + R"(',
                 clientId: 'client_id_1',
+                nonce: '12345',
               }]
             }
           }));
-          return await x.login({nonce: '12345'});
+          return x.idToken;
         }) ()
     )";
 
diff --git a/content/browser/webrtc/resources/dump_creator.js b/content/browser/webrtc/resources/dump_creator.js
index 13aec065..091fad8 100644
--- a/content/browser/webrtc/resources/dump_creator.js
+++ b/content/browser/webrtc/resources/dump_creator.js
@@ -40,37 +40,37 @@
     content.appendChild($('dump-template').content.cloneNode(true));
     content.getElementsByTagName('a')[0].addEventListener(
         'click', this.onDownloadData_.bind(this));
-    content.getElementsByTagName('input')[0].addEventListener(
-        'click', this.onAudioDebugRecordingsChanged_.bind(this));
     content.getElementsByTagName('input')[1].addEventListener(
+        'click', this.onAudioDebugRecordingsChanged_.bind(this));
+    content.getElementsByTagName('input')[2].addEventListener(
         'click', this.onEventLogRecordingsChanged_.bind(this));
   }
 
   // Mark the diagnostic audio recording checkbox checked.
   setAudioDebugRecordingsCheckbox() {
-    this.root_.getElementsByTagName('input')[0].checked = true;
+    this.root_.getElementsByTagName('input')[1].checked = true;
   }
 
   // Mark the diagnostic audio recording checkbox unchecked.
   clearAudioDebugRecordingsCheckbox() {
-    this.root_.getElementsByTagName('input')[0].checked = false;
+    this.root_.getElementsByTagName('input')[1].checked = false;
   }
 
   // Mark the event log recording checkbox checked.
   setEventLogRecordingsCheckbox() {
-    this.root_.getElementsByTagName('input')[1].checked = true;
+    this.root_.getElementsByTagName('input')[2].checked = true;
   }
 
   // Mark the event log recording checkbox unchecked.
   clearEventLogRecordingsCheckbox() {
-    this.root_.getElementsByTagName('input')[1].checked = false;
+    this.root_.getElementsByTagName('input')[2].checked = false;
   }
 
   // Mark the event log recording checkbox as mutable/immutable.
   setEventLogRecordingsCheckboxMutability(mutable) {
     // TODO(eladalon): Remove reliance on number and order of elements.
     // https://crbug.com/817391
-    this.root_.getElementsByTagName('input')[1].disabled = !mutable;
+    this.root_.getElementsByTagName('input')[2].disabled = !mutable;
     if (!mutable) {
       const label = this.root_.getElementsByTagName('label')[2];
       label.style = 'color:red;';
@@ -84,20 +84,34 @@
    *
    * @private
    */
-  onDownloadData_() {
+  async onDownloadData_(event) {
+    const useCompression = this.root_.getElementsByTagName('input')[0].checked;
     const dumpObject = {
       'getUserMedia': userMediaRequests,
       'PeerConnections': peerConnectionDataStore,
       'UserAgent': navigator.userAgent,
     };
     const textBlob =
-        new Blob([JSON.stringify(dumpObject, null, 1)], {type: 'octet/stream'});
-    const URL = window.URL.createObjectURL(textBlob);
-
+      new Blob([JSON.stringify(dumpObject, null, 1)], {type: 'octet/stream'});
+    let url;
+    if (useCompression) {
+      const compressionStream = new CompressionStream('gzip');
+      const binaryStream = textBlob.stream().pipeThrough(compressionStream);
+      const binaryBlob = await new Response(binaryStream).blob();
+      url = URL.createObjectURL(binaryBlob);
+      // Since this is async we can't use the default event and need to click
+      // again (while avoiding an infinite loop).
+      const anchor = document.createElement('a');
+      anchor.download = 'webrtc_internals_dump.gz'
+      anchor.href = url;
+      anchor.click();
+      return;
+    }
+    url = URL.createObjectURL(textBlob);
     const anchor = this.root_.getElementsByTagName('a')[0];
-    anchor.href = URL;
-    anchor.download = 'webrtc_internals_dump.txt';
-    // The default action of the anchor will download the URL.
+    anchor.download = 'webrtc_internals_dump.txt'
+    anchor.href = url;
+    // The default action of the anchor will download the url.
   }
 
   /**
@@ -106,7 +120,7 @@
    * @private
    */
   onAudioDebugRecordingsChanged_() {
-    const enabled = this.root_.getElementsByTagName('input')[0].checked;
+    const enabled = this.root_.getElementsByTagName('input')[1].checked;
     if (enabled) {
       chrome.send('enableAudioDebugRecordings');
     } else {
@@ -120,7 +134,7 @@
    * @private
    */
   onEventLogRecordingsChanged_() {
-    const enabled = this.root_.getElementsByTagName('input')[1].checked;
+    const enabled = this.root_.getElementsByTagName('input')[2].checked;
     if (enabled) {
       chrome.send('enableEventLogRecordings');
     } else {
diff --git a/content/browser/webrtc/resources/webrtc_internals.html b/content/browser/webrtc/resources/webrtc_internals.html
index 4a71a3639..4d6050b5c 100644
--- a/content/browser/webrtc/resources/webrtc_internals.html
+++ b/content/browser/webrtc/resources/webrtc_internals.html
@@ -21,6 +21,9 @@
         <a>
           <button>Download the PeerConnection updates and stats data</button>
         </a>
+        <label>
+          <input type="checkbox">Compress result
+        </label>
       </div>
       <p>
         <label>
diff --git a/content/common/agent_scheduling_group.mojom b/content/common/agent_scheduling_group.mojom
index 6bad15f..341fcf4c 100644
--- a/content/common/agent_scheduling_group.mojom
+++ b/content/common/agent_scheduling_group.mojom
@@ -103,7 +103,8 @@
   //    making the mapping ambiguous and non-authoritative.
   CreateFrameProxy(blink.mojom.RemoteFrameToken token, int32 routing_id,
                    blink.mojom.FrameToken? opener_frame_token,
-                   int32 view_routing_id, int32 parent_routing_id,
+                   int32 view_routing_id,
+                   blink.mojom.RemoteFrameToken? parent_frame_token,
                    blink.mojom.TreeScopeType tree_scope_type,
                    blink.mojom.FrameReplicationState replication_state,
                    mojo_base.mojom.UnguessableToken devtools_frame_token,
diff --git a/content/common/common_param_traits_unittest.cc b/content/common/common_param_traits_unittest.cc
index 04be93d..f42dda0 100644
--- a/content/common/common_param_traits_unittest.cc
+++ b/content/common/common_param_traits_unittest.cc
@@ -71,7 +71,7 @@
   IPC::Message bad_msg(1, 2, IPC::Message::PRIORITY_NORMAL);
   // Copy the first message block over to |bad_msg|.
   const char* fixed_data;
-  size_t fixed_data_size;
+  int fixed_data_size;
   iter = base::PickleIterator(msg);
   EXPECT_TRUE(iter.ReadData(&fixed_data, &fixed_data_size));
   bad_msg.WriteData(fixed_data, fixed_data_size);
diff --git a/content/public/common/common_param_traits.h b/content/public/common/common_param_traits.h
index d6b7164..ad0908f 100644
--- a/content/public/common/common_param_traits.h
+++ b/content/public/common/common_param_traits.h
@@ -65,7 +65,7 @@
     return iter->ReadUInt32(reinterpret_cast<uint32_t*>(r));
 #else
     const char *data;
-    size_t data_size = 0;
+    int data_size = 0;
     bool result = iter->ReadData(&data, &data_size);
     if (result && data_size == sizeof(gfx::NativeWindow)) {
       memcpy(r, data, sizeof(gfx::NativeWindow));
diff --git a/content/renderer/agent_scheduling_group.cc b/content/renderer/agent_scheduling_group.cc
index 9ee3fbc..3c1b24e 100644
--- a/content/renderer/agent_scheduling_group.cc
+++ b/content/renderer/agent_scheduling_group.cc
@@ -293,14 +293,14 @@
     int32_t routing_id,
     const absl::optional<blink::FrameToken>& opener_frame_token,
     int32_t view_routing_id,
-    int32_t parent_routing_id,
+    const absl::optional<blink::RemoteFrameToken>& parent_frame_token,
     blink::mojom::TreeScopeType tree_scope_type,
     blink::mojom::FrameReplicationStatePtr replicated_state,
     const base::UnguessableToken& devtools_frame_token,
     mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
   RenderFrameProxy::CreateFrameProxy(
       *this, token, routing_id, opener_frame_token, view_routing_id,
-      parent_routing_id, tree_scope_type, std::move(replicated_state),
+      parent_frame_token, tree_scope_type, std::move(replicated_state),
       devtools_frame_token, std::move(remote_main_frame_interfaces));
 }
 
diff --git a/content/renderer/agent_scheduling_group.h b/content/renderer/agent_scheduling_group.h
index 3eab50f..fe008d0 100644
--- a/content/renderer/agent_scheduling_group.h
+++ b/content/renderer/agent_scheduling_group.h
@@ -98,7 +98,7 @@
       int32_t routing_id,
       const absl::optional<blink::FrameToken>& opener_frame_token,
       int32_t view_routing_id,
-      int32_t parent_routing_id,
+      const absl::optional<blink::RemoteFrameToken>& parent_frame_token,
       blink::mojom::TreeScopeType tree_scope_type,
       blink::mojom::FrameReplicationStatePtr replicated_state,
       const base::UnguessableToken& devtools_frame_token,
diff --git a/content/renderer/pepper/pepper_url_loader_host.cc b/content/renderer/pepper/pepper_url_loader_host.cc
index 7ec5ac1f..10fb0c9 100644
--- a/content/renderer/pepper/pepper_url_loader_host.cc
+++ b/content/renderer/pepper/pepper_url_loader_host.cc
@@ -7,7 +7,6 @@
 #include <stddef.h>
 
 #include "base/feature_list.h"
-#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
 #include "content/public/common/content_features.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
@@ -184,7 +183,7 @@
   UpdateProgress();
 
   auto message = std::make_unique<PpapiPluginMsg_URLLoader_SendData>();
-  message->WriteData(data, base::checked_cast<size_t>(data_length));
+  message->WriteData(data, data_length);
   SendUpdateToPlugin(std::move(message));
 }
 
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 017f512..3ef98ae 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -83,14 +83,14 @@
     int routing_id,
     const absl::optional<blink::FrameToken>& opener_frame_token,
     int render_view_routing_id,
-    int parent_routing_id,
+    const absl::optional<blink::RemoteFrameToken>& parent_frame_token,
     blink::mojom::TreeScopeType tree_scope_type,
     blink::mojom::FrameReplicationStatePtr replicated_state,
     const base::UnguessableToken& devtools_frame_token,
     mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
-  RenderFrameProxy* parent = nullptr;
-  if (parent_routing_id != MSG_ROUTING_NONE) {
-    parent = RenderFrameProxy::FromRoutingID(parent_routing_id);
+  blink::WebRemoteFrame* parent = nullptr;
+  if (parent_frame_token) {
+    parent = blink::WebRemoteFrame::FromFrameToken(parent_frame_token.value());
     // It is possible that the parent proxy has been detached in this renderer
     // process, just as the parent's real frame was creating this child frame.
     // In this case, do not create the proxy. See https://crbug.com/568670.
@@ -125,13 +125,13 @@
     // Create a frame under an existing parent. The parent is always expected
     // to be a RenderFrameProxy, because navigations initiated by local frames
     // should not wind up here.
-    web_frame = parent->web_frame()->CreateRemoteChild(
+    web_frame = parent->CreateRemoteChild(
         tree_scope_type, blink::WebString::FromUTF8(replicated_state->name),
         replicated_state->frame_policy, proxy.get(),
         proxy->blink_interface_registry_.get(),
         proxy->GetRemoteAssociatedInterfaces(), frame_token,
         devtools_frame_token, opener);
-    render_view = parent->render_view();
+    render_view = RenderViewImpl::FromWebView(parent->View());
   }
 
   proxy->Init(web_frame, render_view);
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 97df76f..b1b0953 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -77,12 +77,13 @@
   // for example, after a cross-process navigation or after the addition of a
   // new frame local to some other process. |routing_id| will be the ID of the
   // newly created RenderFrameProxy. |render_view_routing_id| identifies the
-  // RenderView to be associated with this frame.  |opener|, if supplied, is the
-  // new frame's opener.  |parent_routing_id| is the routing ID of the
-  // RenderFrameProxy to which the new frame is parented.
+  // RenderView to be associated with this frame.  `opener_frame_token`, if
+  // supplied, is the new frame's opener.  `parent_frame_token`, if supplied,
+  // is the frame token of the RenderFrameProxy to which the new frame is
+  // parented.
   //
-  // |parent_routing_id| always identifies a RenderFrameProxy (never a
-  // RenderFrame) because a new child of a local frame should always start out
+  // `parent_frame_token` always identifies a remote frame (never a
+  // local frame) because a new child of a local frame should always start out
   // as a frame, not a proxy.
   static RenderFrameProxy* CreateFrameProxy(
       AgentSchedulingGroup& agent_scheduling_group,
@@ -90,7 +91,7 @@
       int routing_id,
       const absl::optional<blink::FrameToken>& opener_frame_token,
       int render_view_routing_id,
-      int parent_routing_id,
+      const absl::optional<blink::RemoteFrameToken>& parent_frame_token,
       blink::mojom::TreeScopeType tree_scope_type,
       blink::mojom::FrameReplicationStatePtr replicated_state,
       const base::UnguessableToken& devtools_frame_token,
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 5704476f..dec568f 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -155,7 +155,7 @@
     RenderFrameProxy::CreateFrameProxy(
         agent_scheduling_group_, params->main_frame->get_remote_params()->token,
         params->main_frame->get_remote_params()->routing_id,
-        params->opener_frame_token, GetRoutingID(), MSG_ROUTING_NONE,
+        params->opener_frame_token, GetRoutingID(), absl::nullopt,
         blink::mojom::TreeScopeType::kDocument /* ignored for main frames */,
         std::move(params->replication_state), params->devtools_main_frame_token,
         std::move(
@@ -193,7 +193,7 @@
 }
 
 /*static*/
-RenderView* RenderView::FromWebView(blink::WebView* webview) {
+RenderViewImpl* RenderViewImpl::FromWebView(blink::WebView* webview) {
   DCHECK(RenderThread::IsMainThread());
   ViewMap* views = g_view_map.Pointer();
   auto it = views->find(webview);
@@ -201,6 +201,11 @@
 }
 
 /*static*/
+RenderView* RenderView::FromWebView(blink::WebView* webview) {
+  return RenderViewImpl::FromWebView(webview);
+}
+
+/*static*/
 RenderViewImpl* RenderViewImpl::FromRoutingID(int32_t routing_id) {
   DCHECK(RenderThread::IsMainThread());
   RoutingIDViewMap* views = g_routing_id_view_map.Pointer();
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 9b39a5c..d021052 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -99,6 +99,9 @@
   // Returns the RenderViewImpl for the given routing ID.
   static RenderViewImpl* FromRoutingID(int routing_id);
 
+  // Returns the RenderViewImpl for the given blink::WebView.
+  static RenderViewImpl* FromWebView(blink::WebView* web_view);
+
   // blink::WebViewClient implementation --------------------------------------
 
   blink::WebView* CreateView(
diff --git a/content/test/data/browsing_data/storage_accessor.html b/content/test/data/browsing_data/storage_accessor.html
index e255b2e..031d4a90 100644
--- a/content/test/data/browsing_data/storage_accessor.html
+++ b/content/test/data/browsing_data/storage_accessor.html
@@ -22,13 +22,18 @@
       } catch (e) {}
 
       // Access file system.
-      const file_system_promise = new Promise(function(resolve, reject) {
-        window.webkitRequestFileSystem(TEMPORARY, 1024, fs => {
-          fs.root.getFile("foo.txt");
-          resolve();
-        });
+      await new Promise(resolve => {
+        window.webkitRequestFileSystem(TEMPORARY, 1024, function (fs) {
+          fs.root.getFile('foo.txt', { create : true }, fileEntry => {
+            fileEntry.createWriter(function (fileWriter) {
+              fileWriter.onwriteend = () => resolve();
+              let blob = new Blob([1,2,3], { type: 'text/plain' });
+              fileWriter.write(blob);
+            });
+          },
+        );
+        }, () => resolve());
       });
-      await file_system_promise;
 
       // Access service worker.
       await navigator.serviceWorker.register("empty.js");
@@ -37,7 +42,22 @@
       await caches.open('test');
 
       // Access Indexed DB.
-      await indexedDB.open("test");
+      await new Promise(resolve => {
+        let open = indexedDB.open('test');
+        open.onupgradeneeded = () => {
+          open.result.createObjectStore('store', { keyPath: 'id' });
+        }
+        open.onsuccess = () => {
+          let db = open.result;
+          let tx = db.transaction('store', 'readwrite');
+          let store = tx.objectStore('store');
+          store.put({ id: 42, value: 43 });
+          tx.oncomplete = () => {
+            db.close();
+            resolve();
+          };
+        }
+      });
 
       return true;
      }
diff --git a/docs/enterprise/add_new_policy.md b/docs/enterprise/add_new_policy.md
index 216b06a..517cafa 100644
--- a/docs/enterprise/add_new_policy.md
+++ b/docs/enterprise/add_new_policy.md
@@ -19,6 +19,9 @@
 -   Deprecating an old feature. Create a policy to give enterprise users more
     time to migrate away from the feature.
 
+**To read more about best practices for shipping enterprise friendly features
+read [this article](https://www.chromium.org/developers/enterprise-changes/).**
+
 ## Adding a new policy
 
 1.  Think carefully about the name and the desired semantics of the new policy:
diff --git a/docs/ios/multiwindow_eg_tests.md b/docs/ios/multiwindow_eg_tests.md
index e7e48f23..aab146a0 100644
--- a/docs/ios/multiwindow_eg_tests.md
+++ b/docs/ios/multiwindow_eg_tests.md
@@ -233,14 +233,14 @@
   [EarlGrey setRootMatcherForSubsequentInteractions:
                 chrome_test_util::WindowWithNumber(windowNumber)];
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:HistoryButton()];
+  [ChromeEarlGreyUI tapToolsMenuButton:HistoryDestinationButton()];
 ```
 
 Can be reduced to:
 
 ```
   [ChromeEarlGreyUI openToolsMenuInWindowWithNumber:windowNumber];
-  [ChromeEarlGreyUI tapToolsMenuButton:HistoryButton()];
+  [ChromeEarlGreyUI tapToolsMenuButton:HistoryDestinationButton()];
 ```
 
 ## Actions
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 4534d04..a48a920 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -264,9 +264,12 @@
 
 The user interface is localized in the following languages: TBD.
 
-TODO(crbug.com/1014591): Implement install cancellation.
+TODO(crbug.com/1014591): Implement install cancellation, including cancellation
+ping.
 
-The install flow can be stopped before the payload finished downloading.
+The install flow can be stopped before the payload finishes downloading. In this
+case, the event ping associated with the install attempt will be sent with an
+`eventresult` of 4 (cancelled).
 
 TODO(crbug.com/1286580): Implement silent mode.
 
@@ -295,6 +298,9 @@
 The updater communicates with update servers using the
 [Omaha Protocol](protocol_3_1.md).
 
+The updater uses platform-native network stacks (WinHTTP on Windows and
+NSURLSession on macOS).
+
 #### Security
 It is not possible to MITM the updater even if the network (including TLS) is
 compromised. The integrity of the client-server communication is guaranteed
@@ -306,7 +312,8 @@
 next normally scheduled update check.
 
 #### DOS Mitigation
-The updater sends DoS mitigation headers in requests to the server.
+The updater sends [DoS mitigation headers](protocol_3_1.md) in requests to the
+server.
 
 When the server responds with an `X-Retry-After header`, the client does not
 issue another update check until the specified period has passed (maximum 24
@@ -334,6 +341,12 @@
 
 TODO(crbug.com/1035895): Document Windows installer APIs
 
+TODO(crbug.com/1339454): Run installers at BELOW_NORMAL_PRIORITY_CLASS if the
+update flow is a background flow.
+
+### Enterprise Enrollment
+TODO(crbug.com/1339451): Document enterprise enrollment and the token.
+
 ### Enterprise Policies
 Enterprise policies can prevent the installation of applications:
 *   A per-application setting may specify whether an application is installable.
@@ -659,6 +672,22 @@
 There is no download cache. Payloads are re-downloaded for applications which
 fail to install.
 
+### Logging
+All updater logs are written to `{UPDATER_DATA_DIR}\updater.log`.
+
+On macOS for system-scope updaters, `{UPDATER_DATA_DIR}` is
+`/Library/Application Support/{COMPANY_SHORTNAME}/{PRODUCT_FULLNAME}`.
+
+On macOS for user-scope updaters, `{UPDATER_DATA_DIR}` is
+`~/Library/Application Support/{COMPANY_SHORTNAME}/{PRODUCT_FULLNAME}`.
+
+On Windows for system-scope updaters, `{UPDATER_DATA_DIR}` is
+`%PROGRAMFILES%\{COMPANY_SHORTNAME}\{PRODUCT_FULLNAME}`. (A 32-bit updater uses
+use `%PROGRAMFILESX86%` if appropriate instead.)
+
+On Windows for user-scope updaters, `{UPDATER_DATA_DIR}` is
+`%LOCALAPPDATA%\{COMPANY_SHORTNAME}\{PRODUCT_FULLNAME}`.
+
 ## Services
 
 ### Crash Reporting
diff --git a/extensions/browser/extension_api_frame_id_map.cc b/extensions/browser/extension_api_frame_id_map.cc
index 2f59634..df1f60a2 100644
--- a/extensions/browser/extension_api_frame_id_map.cc
+++ b/extensions/browser/extension_api_frame_id_map.cc
@@ -80,7 +80,7 @@
 int ExtensionApiFrameIdMap::GetFrameId(content::RenderFrameHost* rfh) {
   if (!rfh)
     return kInvalidFrameId;
-  if (!rfh->IsInPrimaryMainFrame())
+  if (rfh->GetParentOrOuterDocument())
     return rfh->GetFrameTreeNodeId();
   return kTopFrameId;
 }
@@ -88,7 +88,7 @@
 // static
 int ExtensionApiFrameIdMap::GetFrameId(
     content::NavigationHandle* navigation_handle) {
-  return navigation_handle->IsInPrimaryMainFrame()
+  return !navigation_handle->GetParentFrameOrOuterDocument()
              ? kTopFrameId
              : navigation_handle->GetFrameTreeNodeId();
 }
diff --git a/extensions/browser/updater/extension_downloader.cc b/extensions/browser/updater/extension_downloader.cc
index 8e3bffd5..8de2a9f 100644
--- a/extensions/browser/updater/extension_downloader.cc
+++ b/extensions/browser/updater/extension_downloader.cc
@@ -192,13 +192,11 @@
     const GURL& url,
     const std::string& package_hash,
     const std::string& version,
-    const std::set<int>& request_ids,
     const DownloadFetchPriority fetch_priority)
     : id(task.id),
       url(url),
       package_hash(package_hash),
       version(version),
-      request_ids(request_ids),
       fetch_priority(fetch_priority),
       credentials(CREDENTIALS_NONE),
       oauth2_attempt_count(0) {
@@ -207,6 +205,13 @@
 
 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() = default;
 
+std::set<int> ExtensionDownloader::ExtensionFetch::GetRequestIds() const {
+  std::set<int> request_ids;
+  for (const ExtensionDownloaderTask& task : associated_tasks)
+    request_ids.insert(task.request_id);
+  return request_ids;
+}
+
 ExtensionDownloader::FetchDataGroupKey::FetchDataGroupKey() = default;
 
 ExtensionDownloader::FetchDataGroupKey::FetchDataGroupKey(
@@ -581,6 +586,7 @@
         net::SiteForCookies::FromUrl(active_request->full_url());
   }
 
+  DCHECK(!manifest_loader_);
   manifest_loader_ = network::SimpleURLLoader::Create(
       std::move(resource_request), traffic_annotation);
   // Update checks can be interrupted if a network change is detected; this is
@@ -660,8 +666,7 @@
           id, ExtensionDownloaderDelegate::Stage::FINISHED);
       auto extension_fetch_data(std::make_unique<ExtensionFetch>(
           std::move(task), fetch_data->base_url(), /*hash not fetched*/ "",
-          /*version not fetched*/ "", fetch_data->request_ids(),
-          fetch_data->fetch_priority()));
+          /*version not fetched*/ "", fetch_data->fetch_priority()));
       NotifyDelegateDownloadFinished(std::move(extension_fetch_data), true,
                                      cached_crx_path.value(), false);
       extensions_fetched_from_cache.insert(id);
@@ -679,12 +684,12 @@
 }
 
 void ExtensionDownloader::RetryRequestOrHandleFailureOnManifestFetchFailure(
-    const network::SimpleURLLoader* loader,
+    const network::SimpleURLLoader& loader,
     const int response_code) {
   bool all_force_installed_extensions =
       manifests_queue_.active_request()->is_all_external_policy_download();
 
-  const int net_error = manifest_loader_->NetError();
+  const int net_error = loader.NetError();
   const int request_failure_count =
       manifests_queue_.active_request_failure_count();
   // If the device is offline, do not retry for force installed extensions,
@@ -698,11 +703,11 @@
       RetryManifestFetchRequest(net_error, response_code);
     return;
   }
-  if (ShouldRetryRequest(loader) && request_failure_count < kMaxRetries) {
-    RetryManifestFetchRequest(loader->NetError(), response_code);
+  if (ShouldRetryRequest(&loader) && request_failure_count < kMaxRetries) {
+    RetryManifestFetchRequest(loader.NetError(), response_code);
     return;
   }
-  const GURL url = loader->GetFinalURL();
+  const GURL url = loader.GetFinalURL();
   RETRY_HISTOGRAM("ManifestFetchFailure", request_failure_count, url);
   if (all_force_installed_extensions) {
     if (TryFetchingExtensionsFromCache(manifests_queue_.active_request()))
@@ -728,13 +733,17 @@
 
 void ExtensionDownloader::OnManifestLoadComplete(
     std::unique_ptr<std::string> response_body) {
-  const GURL url = manifest_loader_->GetFinalURL();
+  // Move loader from class-wide field to the local variable in order to make
+  // ExtensionDownloader reentrable.
+  std::unique_ptr<network::SimpleURLLoader> loader =
+      std::move(manifest_loader_);
+  const GURL url = loader->GetFinalURL();
   DCHECK(manifests_queue_.active_request());
+  DCHECK(loader);
 
   int response_code = -1;
-  if (manifest_loader_->ResponseInfo() &&
-      manifest_loader_->ResponseInfo()->headers)
-    response_code = manifest_loader_->ResponseInfo()->headers->response_code();
+  if (loader->ResponseInfo() && loader->ResponseInfo()->headers)
+    response_code = loader->ResponseInfo()->headers->response_code();
 
   VLOG(2) << response_code << " " << url;
 
@@ -756,10 +765,8 @@
   } else {
     VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
             << "' response code:" << response_code;
-    RetryRequestOrHandleFailureOnManifestFetchFailure(manifest_loader_.get(),
-                                                      response_code);
+    RetryRequestOrHandleFailureOnManifestFetchFailure(*loader, response_code);
   }
-  manifest_loader_.reset();
   file_url_loader_factory_.reset();
   manifests_queue_.reset_active_request();
 
@@ -823,8 +830,7 @@
     FetchUpdatedExtension(
         std::make_unique<ExtensionFetch>(
             std::move(update.first), crx_url, update.second->package_hash,
-            update.second->version, fetch_data->request_ids(),
-            fetch_data->fetch_priority()),
+            update.second->version, fetch_data->fetch_priority()),
         update.second->info);
   }
 
@@ -1076,11 +1082,11 @@
       // case of no updates status in the update manifest.
       ExtensionDownloaderDelegate::FailureData data(info.value_or(""));
       NotifyExtensionsDownloadFailedWithFailureData(
-          {fetch_data->id}, fetch_data->request_ids,
+          {fetch_data->id}, fetch_data->GetRequestIds(),
           ExtensionDownloaderDelegate::Error::CRX_FETCH_URL_EMPTY, data);
     } else {
       NotifyExtensionsDownloadFailed(
-          {fetch_data->id}, fetch_data->request_ids,
+          {fetch_data->id}, fetch_data->GetRequestIds(),
           ExtensionDownloaderDelegate::Error::CRX_FETCH_URL_INVALID);
     }
     return;
@@ -1096,8 +1102,6 @@
           iter->associated_tasks.end(),
           std::make_move_iterator(fetch_data->associated_tasks.begin()),
           std::make_move_iterator(fetch_data->associated_tasks.end()));
-      iter->request_ids.insert(fetch_data->request_ids.begin(),
-                               fetch_data->request_ids.end());
       return;  // already scheduled
     }
   }
@@ -1110,8 +1114,6 @@
         extensions_queue_.active_request()->associated_tasks.end(),
         std::make_move_iterator(fetch_data->associated_tasks.begin()),
         std::make_move_iterator(fetch_data->associated_tasks.end()));
-    extensions_queue_.active_request()->request_ids.insert(
-        fetch_data->request_ids.begin(), fetch_data->request_ids.end());
     return;
   }
   absl::optional<base::FilePath> cached_crx_path =
@@ -1139,7 +1141,7 @@
   const std::string& package_hash = fetch_data->package_hash;
   const GURL& url = fetch_data->url;
   const base::Version& version = fetch_data->version;
-  const std::set<int>& request_ids = fetch_data->request_ids;
+  const std::set<int> request_ids = fetch_data->GetRequestIds();
   const crx_file::VerifierFormat required_format =
       extension_urls::IsWebstoreUpdateUrl(fetch_data->url)
           ? GetWebstoreVerifierFormat(false)
@@ -1283,12 +1285,6 @@
   const base::TimeDelta& backoff_delay = base::Milliseconds(0);
 
   ExtensionFetch& active_request = *extensions_queue_.active_request();
-#if DCHECK_IS_ON()
-  std::set<int> tasks_request_ids;
-  for (const ExtensionDownloaderTask& task : active_request.associated_tasks)
-    tasks_request_ids.insert(task.request_id);
-  DCHECK(active_request.request_ids == tasks_request_ids);
-#endif
   const ExtensionId& id = active_request.id;
   if (!crx_path.empty()) {
     RETRY_HISTOGRAM("CrxFetchSuccess",
@@ -1312,7 +1308,7 @@
     extensions_queue_.RetryRequest(backoff_delay);
     delegate_->OnExtensionDownloadRetryForTests();
   } else {
-    const std::set<int>& request_ids = active_request.request_ids;
+    const std::set<int> request_ids = active_request.GetRequestIds();
     const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id];
     VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
             << "' response code:" << response_code;
diff --git a/extensions/browser/updater/extension_downloader.h b/extensions/browser/updater/extension_downloader.h
index 10c9973..f54d238 100644
--- a/extensions/browser/updater/extension_downloader.h
+++ b/extensions/browser/updater/extension_downloader.h
@@ -180,17 +180,16 @@
                    const GURL& url,
                    const std::string& package_hash,
                    const std::string& version,
-                   const std::set<int>& request_ids,
                    DownloadFetchPriority fetch_priority);
     ~ExtensionFetch();
 
+    // Collects request ids from associated tasks.
+    std::set<int> GetRequestIds() const;
+
     ExtensionId id;
     GURL url;
     std::string package_hash;
     base::Version version;
-    // TODO(b:235968596): Remove `request_ids` from this struct, as we have all
-    // data needed in the associated tasks.
-    std::set<int> request_ids;
     DownloadFetchPriority fetch_priority;
     std::vector<ExtensionDownloaderTask> associated_tasks;
 
@@ -273,7 +272,7 @@
   // AddFailureDataOnManifestFetchFailed when fetching of update manifest
   // failed.
   void RetryRequestOrHandleFailureOnManifestFetchFailure(
-      const network::SimpleURLLoader* loader,
+      const network::SimpleURLLoader& loader,
       const int response_code);
 
   // Handles the result of a manifest fetch.
diff --git a/extensions/renderer/user_script_set.cc b/extensions/renderer/user_script_set.cc
index 9528bc1..b5fa2c6 100644
--- a/extensions/renderer/user_script_set.cc
+++ b/extensions/renderer/user_script_set.cc
@@ -135,15 +135,15 @@
     // gets cleared up when the last renderer or browser process drops their
     // reference to the shared memory.
     for (size_t j = 0; j < script->js_scripts().size(); ++j) {
-      const char* body = nullptr;
-      size_t body_length = 0;
+      const char* body = NULL;
+      int body_length = 0;
       CHECK(iter.ReadData(&body, &body_length));
       script->js_scripts()[j]->set_external_content(
           base::StringPiece(body, body_length));
     }
     for (size_t j = 0; j < script->css_scripts().size(); ++j) {
-      const char* body = nullptr;
-      size_t body_length = 0;
+      const char* body = NULL;
+      int body_length = 0;
       CHECK(iter.ReadData(&body, &body_length));
       script->css_scripts()[j]->set_external_content(
           base::StringPiece(body, body_length));
diff --git a/fuchsia_web/runners/BUILD.gn b/fuchsia_web/runners/BUILD.gn
index 432ad10..d9bd900 100644
--- a/fuchsia_web/runners/BUILD.gn
+++ b/fuchsia_web/runners/BUILD.gn
@@ -103,6 +103,8 @@
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web",
   ]
 
+  visibility += [ "//fuchsia_web/runners/cast/test:*" ]
+
   # Keep CastRunner trim by ensuring that large browser dependencies don't creep in.
   # TODO(crbug.com/1211328): Migrate //fuchsia_web/webengine/web_instance_host to have
   # each web_instance set the command-line options internally, so that no-deps
@@ -226,6 +228,9 @@
 test("cast_runner_integration_tests_cfv1") {
   sources = [
     "cast/cast_runner_integration_test.cc",
+    "cast/cast_runner_integration_test_base.cc",
+    "cast/cast_runner_integration_test_base.h",
+    "cast/cast_runner_integration_test_cfv1.cc",
     "common/modular/fake_component_context.cc",
     "common/modular/fake_component_context.h",
   ]
@@ -237,8 +242,8 @@
     "//components/cast/message_port",
     "//fuchsia_web/common/test:run_all_integration_tests",
     "//fuchsia_web/common/test:test_support",
+    "//fuchsia_web/runners/cast/test:cfv1_integration",
     "//fuchsia_web/runners/common/modular",
-    "//media/fuchsia/audio:test_support",
     "//net:test_support",
     "//testing/gtest",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
diff --git a/fuchsia_web/runners/cast/DEPS b/fuchsia_web/runners/cast/DEPS
index 3c18598..2606999c 100644
--- a/fuchsia_web/runners/cast/DEPS
+++ b/fuchsia_web/runners/cast/DEPS
@@ -15,8 +15,4 @@
     "+fuchsia_web/webengine/test/frame_for_test.h",
     "+fuchsia_web/webengine/test/web_engine_browser_test.h",
   ],
-  ".*_integration_test\.cc": [
-    # Audio test helpers.
-    "+media/fuchsia/audio/fake_audio_device_enumerator.h",
-  ],
 }
diff --git a/fuchsia_web/runners/cast/cast_component.cc b/fuchsia_web/runners/cast/cast_component.cc
index f9e9129..4360746 100644
--- a/fuchsia_web/runners/cast/cast_component.cc
+++ b/fuchsia_web/runners/cast/cast_component.cc
@@ -104,14 +104,12 @@
 
 void CastComponent::ConnectAudio(
     fidl::InterfaceRequest<fuchsia::media::Audio> request) {
-  agent_manager_->ConnectToAgentService(application_config_.agent_url(),
-                                        std::move(request));
+  startup_context()->svc()->Connect(std::move(request));
 }
 
 void CastComponent::ConnectDeviceWatcher(
     fidl::InterfaceRequest<fuchsia::camera3::DeviceWatcher> request) {
-  agent_manager_->ConnectToAgentService(application_config_.agent_url(),
-                                        std::move(request));
+  startup_context()->svc()->Connect(std::move(request));
 }
 
 bool CastComponent::HasWebPermission(
diff --git a/fuchsia_web/runners/cast/cast_component.h b/fuchsia_web/runners/cast/cast_component.h
index 50a973e9..d5134a8 100644
--- a/fuchsia_web/runners/cast/cast_component.h
+++ b/fuchsia_web/runners/cast/cast_component.h
@@ -131,7 +131,10 @@
   const bool is_headless_;
   base::OnceClosure on_destroyed_;
 
+  // Maintains an active connection to the Agent responsible for this component,
+  // to ensure that per-component state is kept live.
   std::unique_ptr<cr_fuchsia::AgentManager> agent_manager_;
+
   chromium::cast::ApplicationConfig application_config_;
   chromium::cast::UrlRequestRewriteRulesProviderPtr url_rewrite_rules_provider_;
   std::vector<fuchsia::web::UrlRequestRewriteRule> initial_url_rewrite_rules_;
diff --git a/fuchsia_web/runners/cast/cast_runner_integration_test.cc b/fuchsia_web/runners/cast/cast_runner_integration_test.cc
index b3afe09..40108be 100644
--- a/fuchsia_web/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia_web/runners/cast/cast_runner_integration_test.cc
@@ -27,7 +27,6 @@
 #include "base/fuchsia/filtered_service_directory.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/mem_buffer_util.h"
-#include "base/fuchsia/process_context.h"
 #include "base/fuchsia/scoped_service_binding.h"
 #include "base/fuchsia/test_component_controller.h"
 #include "base/path_service.h"
@@ -47,15 +46,13 @@
 #include "fuchsia_web/common/test/test_devtools_list_fetcher.h"
 #include "fuchsia_web/common/test/url_request_rewrite_test_util.h"
 #include "fuchsia_web/runners/cast/cast_runner.h"
+#include "fuchsia_web/runners/cast/cast_runner_integration_test_base.h"
 #include "fuchsia_web/runners/cast/cast_runner_switches.h"
 #include "fuchsia_web/runners/cast/fake_api_bindings.h"
 #include "fuchsia_web/runners/cast/fake_application_config_manager.h"
 #include "fuchsia_web/runners/cast/fidl/fidl/chromium/cast/cpp/fidl.h"
 #include "fuchsia_web/runners/common/modular/agent_impl.h"
 #include "fuchsia_web/runners/common/modular/fake_component_context.h"
-#include "media/fuchsia/audio/fake_audio_device_enumerator.h"
-#include "net/test/embedded_test_server/default_handlers.h"
-#include "net/test/embedded_test_server/http_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -66,15 +63,9 @@
 constexpr char kBlankAppUrl[] = "/defaultresponse";
 constexpr char kEchoHeaderPath[] = "/echoheader?Test";
 
-constexpr char kTestServerRoot[] = "fuchsia_web/runners/cast/testdata";
-
 constexpr char kDummyAgentUrl[] =
     "fuchsia-pkg://fuchsia.com/dummy_agent#meta/dummy_agent.cmx";
 
-constexpr char kEnableFrameHostComponent[] = "enable-frame-host-component";
-
-constexpr char kEnableCfv1Shim[] = "enable-cfv1-shim";
-
 class FakeCorsExemptHeaderProvider final
     : public chromium::cast::CorsExemptHeaderProvider {
  public:
@@ -234,9 +225,9 @@
 
 class TestCastComponent {
  public:
-  explicit TestCastComponent(fuchsia::sys::Runner* cast_runner)
+  explicit TestCastComponent(fuchsia::sys::RunnerPtr& cast_runner)
       : app_config_manager_binding_(&component_services_, &app_config_manager_),
-        cast_runner_(cast_runner) {
+        cast_runner_(cast_runner.get()) {
     EXPECT_TRUE(cast_runner_);
   }
 
@@ -521,151 +512,14 @@
   fuchsia::sys::Runner* const cast_runner_;
 };
 
-enum CastRunnerFeatures {
-  kCastRunnerFeaturesNone = 0,
-  kCastRunnerFeaturesHeadless = 1,
-  kCastRunnerFeaturesVulkan = 1 << 1,
-  kCastRunnerFeaturesFrameHost = 1 << 2,
-  kCastRunnerFeaturesFakeAudioDeviceEnumerator = 1 << 3,
-  kCastRunnerFeaturesCfv1Shim = 1 << 4,
-};
-
 }  // namespace
 
-class CastRunnerIntegrationTest : public testing::Test {
- public:
-  CastRunnerIntegrationTest()
-      : CastRunnerIntegrationTest(kCastRunnerFeaturesNone) {}
-
-  CastRunnerIntegrationTest(const CastRunnerIntegrationTest&) = delete;
-  CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) =
-      delete;
-
-  void TearDown() override {
-    // Unbind the Runner channel, to prevent it from triggering an error when
-    // the CastRunner and WebEngine are torn down.
-    cast_runner_.Unbind();
-  }
-
-  const sys::ServiceDirectory& cast_runner_services() {
-    return *cast_runner_services_;
-  }
-
- protected:
-  explicit CastRunnerIntegrationTest(CastRunnerFeatures runner_features) {
-    // Start CastRunner.
-    cast_runner_services_ = StartCastRunner(
-        runner_features, cast_runner_controller_.ptr().NewRequest());
-
-    // Connect to the CastRunner's fuchsia.sys.Runner interface.
-    cast_runner_ = cast_runner_services().Connect<fuchsia::sys::Runner>();
-    cast_runner_.set_error_handler([](zx_status_t status) {
-      ZX_LOG(ERROR, status) << "CastRunner closed channel.";
-      ADD_FAILURE();
-    });
-
-    test_server_.ServeFilesFromSourceDirectory(kTestServerRoot);
-    net::test_server::RegisterDefaultHandlers(&test_server_);
-    EXPECT_TRUE(test_server_.Start());
-  }
-
-  std::unique_ptr<sys::ServiceDirectory> StartCastRunner(
-      CastRunnerFeatures runner_features,
-      fidl::InterfaceRequest<fuchsia::sys::ComponentController>
-          component_controller_request) {
-    fuchsia::sys::LaunchInfo launch_info;
-    launch_info.url =
-        "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx";
-
-    // Clone stderr from the current process to CastRunner and ask it to
-    // redirect all logs to stderr.
-    launch_info.err = fuchsia::sys::FileDescriptor::New();
-    launch_info.err->type0 = PA_FD;
-    zx_status_t status = fdio_fd_clone(
-        STDERR_FILENO, launch_info.err->handle0.reset_and_get_address());
-    ZX_CHECK(status == ZX_OK, status);
-
-    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-    command_line.AppendSwitchASCII("enable-logging", "stderr");
-
-    if (runner_features & kCastRunnerFeaturesHeadless)
-      command_line.AppendSwitch(kForceHeadlessForTestsSwitch);
-    if (!(runner_features & kCastRunnerFeaturesVulkan))
-      command_line.AppendSwitch(kDisableVulkanForTestsSwitch);
-    if (runner_features & kCastRunnerFeaturesFrameHost)
-      command_line.AppendSwitch(kEnableFrameHostComponent);
-    if (runner_features & kCastRunnerFeaturesCfv1Shim)
-      command_line.AppendSwitch(kEnableCfv1Shim);
-
-    // Add all switches and arguments, skipping the program.
-    launch_info.arguments.emplace(std::vector<std::string>(
-        command_line.argv().begin() + 1, command_line.argv().end()));
-
-    std::unique_ptr<fuchsia::sys::ServiceList> additional_services =
-        std::make_unique<fuchsia::sys::ServiceList>();
-    auto* svc_dir = services_for_cast_runner_.GetOrCreateDirectory("svc");
-    if (runner_features & kCastRunnerFeaturesFakeAudioDeviceEnumerator) {
-      fake_audio_device_enumerator_ =
-          std::make_unique<media::FakeAudioDeviceEnumerator>(svc_dir);
-      additional_services->names.push_back(
-          fuchsia::media::AudioDeviceEnumerator::Name_);
-    }
-    if (runner_features & kCastRunnerFeaturesCfv1Shim) {
-      additional_services->names.push_back("fuchsia.sys.Runner-cast");
-    }
-
-    fuchsia::io::DirectoryHandle svc_dir_handle;
-    svc_dir->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
-                       fuchsia::io::OpenFlags::RIGHT_WRITABLE,
-                   svc_dir_handle.NewRequest().TakeChannel());
-    additional_services->host_directory = svc_dir_handle.TakeChannel();
-
-    launch_info.additional_services = std::move(additional_services);
-
-    fuchsia::io::DirectoryHandle cast_runner_services_dir;
-    launch_info.directory_request =
-        cast_runner_services_dir.NewRequest().TakeChannel();
-
-    fuchsia::sys::LauncherPtr launcher;
-    base::ComponentContextForProcess()->svc()->Connect(launcher.NewRequest());
-    launcher->CreateComponent(std::move(launch_info),
-                              std::move(component_controller_request));
-    return std::make_unique<sys::ServiceDirectory>(
-        std::move(cast_runner_services_dir));
-  }
-
-  base::test::SingleThreadTaskEnvironment task_environment_{
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
-  net::EmbeddedTestServer test_server_;
-
-  // TODO(https://crbug.com/1168538): Override the RunLoop timeout set by
-  // |task_environment_| to allow for the very high variability in web.Context
-  // launch times.
-  const base::test::ScopedRunLoopTimeout scoped_timeout_{
-      FROM_HERE, TestTimeouts::action_max_timeout()};
-
-  base::TestComponentController web_engine_controller_;
-  base::TestComponentController cast_runner_controller_;
-
-  // Directory used to publish test ContextProvider to CastRunner. Some tests
-  // restart ContextProvider, so we can't pass the services directory from
-  // ContextProvider to CastRunner directly.
-  sys::OutgoingDirectory services_for_cast_runner_;
-
-  std::unique_ptr<media::FakeAudioDeviceEnumerator>
-      fake_audio_device_enumerator_;
-
-  fuchsia::sys::RunnerPtr cast_runner_;
-
-  std::unique_ptr<sys::ServiceDirectory> cast_runner_services_;
-};
-
 // A basic integration test ensuring a basic cast request launches the right
 // URL in the Chromium service.
 TEST_F(CastRunnerIntegrationTest, BasicRequest) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
 
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, app_url);
   component.CreateComponentContextAndStartComponent();
 
@@ -682,8 +536,8 @@
 //   WebEngine component instance, only the ContextProvider, which will not
 //   result in the WebEngine instance being torn-down.
 TEST_F(CastRunnerIntegrationTest, DISABLED_CanRecreateContext) {
-  TestCastComponent component(cast_runner_.get());
-  const GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  const GURL app_url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, app_url);
 
   // Create a Cast component and verify that it has loaded.
@@ -693,7 +547,6 @@
   // Terminate the component that provides the ContextProvider service and
   // wait for the Cast component to terminate, without allowing the message-
   // loop to spin in-between.
-  web_engine_controller_.ptr()->Kill();
   component.WaitForComponentDestroyed();
 
   // Create a second Cast component and verify that it has loaded.
@@ -701,16 +554,16 @@
   // disconnecting yet, so attempts to launch Cast components could fail.
   // WebContentRunner::CreateFrameWithParams() will synchronously verify that
   // the web.Context is not-yet-closed, to work-around that.
-  TestCastComponent second_component(cast_runner_.get());
+  TestCastComponent second_component(cast_runner());
   second_component.app_config_manager()->AddApp(kTestAppId, app_url);
   second_component.CreateComponentContextAndStartComponent(kTestAppId);
   second_component.CheckAppUrl(app_url);
 }
 
 TEST_F(CastRunnerIntegrationTest, ApiBindings) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   component.app_config_manager()->AddApp(kTestAppId,
-                                         test_server_.GetURL(kBlankAppUrl));
+                                         test_server().GetURL(kBlankAppUrl));
 
   component.CreateComponentContextAndStartComponent();
 
@@ -720,7 +573,7 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const char kIncorrectComponentUrl[] = "cast:99999999";
 
   component.CreateComponentContext(kIncorrectComponentUrl);
@@ -733,8 +586,8 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, UrlRequestRewriteRulesProvider) {
-  TestCastComponent component(cast_runner_.get());
-  GURL echo_app_url = test_server_.GetURL(kEchoHeaderPath);
+  TestCastComponent component(cast_runner());
+  GURL echo_app_url = test_server().GetURL(kEchoHeaderPath);
   component.app_config_manager()->AddApp(kTestAppId, echo_app_url);
 
   component.CreateComponentContextAndStartComponent();
@@ -746,9 +599,9 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, ApplicationControllerBound) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   component.app_config_manager()->AddApp(kTestAppId,
-                                         test_server_.GetURL(kBlankAppUrl));
+                                         test_server().GetURL(kBlankAppUrl));
 
   component.CreateComponentContextAndStartComponent();
 
@@ -760,8 +613,8 @@
 
 // Verify an App launched with remote debugging enabled is properly reachable.
 TEST_F(CastRunnerIntegrationTest, RemoteDebugging) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   auto app_config =
       FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
   app_config.set_enable_remote_debugging(true);
@@ -782,7 +635,7 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, IsolatedContext) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const GURL kContentDirectoryUrl("fuchsia-dir://testdata/empty.html");
 
   component.RegisterAppWithTestData(kContentDirectoryUrl);
@@ -792,9 +645,9 @@
 
 // Test the lack of CastAgent service does not cause a CastRunner crash.
 TEST_F(CastRunnerIntegrationTest, NoCastAgent) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   component.app_config_manager()->AddApp(kTestAppId,
-                                         test_server_.GetURL(kEchoHeaderPath));
+                                         test_server().GetURL(kEchoHeaderPath));
 
   component.StartCastComponent(base::StrCat({"cast:", kTestAppId}));
 
@@ -803,9 +656,9 @@
 
 // Test the CastAgent disconnecting does not cause a CastRunner crash.
 TEST_F(CastRunnerIntegrationTest, DisconnectedCastAgent) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   component.app_config_manager()->AddApp(kTestAppId,
-                                         test_server_.GetURL(kEchoHeaderPath));
+                                         test_server().GetURL(kEchoHeaderPath));
 
   component.CreateComponentContextAndStartComponent();
 
@@ -821,7 +674,7 @@
 // AppConfigManager is the one used to retrieve the bindings and the rewrite
 // rules.
 TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrl) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
 
   // These are part of the secondary agent, and CastRunner will contact
   // the secondary agent for both of them.
@@ -830,7 +683,7 @@
 
   // Indicate that this app is to get bindings from a secondary agent.
   auto app_config = FakeApplicationConfigManager::CreateConfig(
-      kTestAppId, test_server_.GetURL(kBlankAppUrl));
+      kTestAppId, test_server().GetURL(kBlankAppUrl));
   app_config.set_agent_url(kDummyAgentUrl);
   component.app_config_manager()->AddAppConfig(std::move(app_config));
 
@@ -884,12 +737,12 @@
 // created. Further validate that the primary agent does not provide ApiBindings
 // or RewriteRules.
 TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrlRewriteOptional) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   FakeApiBindingsImpl dummy_agent_api_bindings;
 
   // Indicate that this app is to get bindings from a secondary agent.
   auto app_config = FakeApplicationConfigManager::CreateConfig(
-      kTestAppId, test_server_.GetURL(kBlankAppUrl));
+      kTestAppId, test_server().GetURL(kBlankAppUrl));
   app_config.set_agent_url(kDummyAgentUrl);
   component.app_config_manager()->AddAppConfig(std::move(app_config));
 
@@ -940,12 +793,12 @@
  public:
   AudioCastRunnerIntegrationTest()
       : CastRunnerIntegrationTest(
-            kCastRunnerFeaturesFakeAudioDeviceEnumerator) {}
+            test::kCastRunnerFeaturesFakeAudioDeviceEnumerator) {}
 };
 
 TEST_F(AudioCastRunnerIntegrationTest, MicrophoneRedirect) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL("/microphone.html");
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL("/microphone.html");
   auto app_config =
       FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
 
@@ -959,7 +812,7 @@
   // Expect fuchsia.media.Audio connection to be redirected to the agent.
   base::RunLoop run_loop;
   ASSERT_EQ(
-      component.component_state()->outgoing_directory()->AddPublicService(
+      component.component_services()->AddPublicService(
           std::make_unique<vfs::Service>(
               [quit_closure = run_loop.QuitClosure()](
                   zx::channel channel, async_dispatcher_t* dispatcher) mutable {
@@ -974,8 +827,8 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, CameraRedirect) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL("/camera.html");
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL("/camera.html");
   auto app_config =
       FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
 
@@ -990,7 +843,7 @@
   // agent.
   bool received_device_watcher_request = false;
   ASSERT_EQ(
-      component.component_state()->outgoing_directory()->AddPublicService(
+      component.component_services()->AddPublicService(
           std::make_unique<vfs::Service>(
               [&received_device_watcher_request](
                   zx::channel channel, async_dispatcher_t* dispatcher) mutable {
@@ -1004,8 +857,8 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, CameraAccessAfterComponentShutdown) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL("/camera.html");
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL("/camera.html");
 
   // First app with camera permission.
   auto app_config =
@@ -1017,7 +870,7 @@
 
   // Second app without camera permission (but it will still try to access
   // fuchsia.camera3.DeviceWatcher service to enumerate devices).
-  TestCastComponent second_component(cast_runner_.get());
+  TestCastComponent second_component(cast_runner());
   auto app_config_2 =
       FakeApplicationConfigManager::CreateConfig(kSecondTestAppId, app_url);
   second_component.app_config_manager()->AddAppConfig(std::move(app_config_2));
@@ -1035,10 +888,10 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, MultipleComponentsUsingCamera) {
-  TestCastComponent first_component(cast_runner_.get());
-  TestCastComponent second_component(cast_runner_.get());
+  TestCastComponent first_component(cast_runner());
+  TestCastComponent second_component(cast_runner());
 
-  GURL app_url = test_server_.GetURL("/camera.html");
+  GURL app_url = test_server().GetURL("/camera.html");
 
   // Start two apps, both with camera permission.
   auto app_config1 =
@@ -1065,15 +918,13 @@
   // agent.
   bool received_device_watcher_request = false;
   ASSERT_EQ(
-      second_component.component_state()
-          ->outgoing_directory()
-          ->AddPublicService(std::make_unique<vfs::Service>(
-                                 [&received_device_watcher_request](
-                                     zx::channel channel,
-                                     async_dispatcher_t* dispatcher) mutable {
-                                   received_device_watcher_request = true;
-                                 }),
-                             fuchsia::camera3::DeviceWatcher::Name_),
+      second_component.component_services()->AddPublicService(
+          std::make_unique<vfs::Service>(
+              [&received_device_watcher_request](
+                  zx::channel channel, async_dispatcher_t* dispatcher) mutable {
+                received_device_watcher_request = true;
+              }),
+          fuchsia::camera3::DeviceWatcher::Name_),
       ZX_OK);
 
   second_component.ExecuteJavaScript("connectCamera();");
@@ -1083,16 +934,16 @@
 class HeadlessCastRunnerIntegrationTest : public CastRunnerIntegrationTest {
  public:
   HeadlessCastRunnerIntegrationTest()
-      : CastRunnerIntegrationTest(kCastRunnerFeaturesHeadless) {}
+      : CastRunnerIntegrationTest(test::kCastRunnerFeaturesHeadless) {}
 };
 
 // A basic integration test ensuring a basic cast request launches the right
 // URL in the Chromium service.
 TEST_F(HeadlessCastRunnerIntegrationTest, Headless) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
 
   const char kAnimationPath[] = "/css_animation.html";
-  const GURL animation_url = test_server_.GetURL(kAnimationPath);
+  const GURL animation_url = test_server().GetURL(kAnimationPath);
   component.app_config_manager()->AddApp(kTestAppId, animation_url);
 
   component.CreateComponentContextAndStartComponent();
@@ -1112,7 +963,7 @@
 
 // Isolated *and* headless? Doesn't sound like much fun!
 TEST_F(HeadlessCastRunnerIntegrationTest, IsolatedAndHeadless) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const GURL kContentDirectoryUrl("fuchsia-dir://testdata/empty.html");
 
   component.RegisterAppWithTestData(kContentDirectoryUrl);
@@ -1123,8 +974,8 @@
 // Verifies that the Context can establish a connection to the Agent's
 // MetricsRecorder service.
 TEST_F(CastRunnerIntegrationTest, LegacyMetricsRedirect) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, app_url);
 
   auto component_url = base::StrCat({"cast:", kTestAppId});
@@ -1152,8 +1003,8 @@
 // Verifies that the ApplicationContext::OnApplicationTerminated() is notified
 // with the component exit code if the web content closes itself.
 TEST_F(CastRunnerIntegrationTest, OnApplicationTerminated_WindowClose) {
-  TestCastComponent component(cast_runner_.get());
-  const GURL url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  const GURL url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, url);
 
   component.CreateComponentContextAndStartComponent();
@@ -1175,8 +1026,8 @@
 // exit code ZX_OK if the web content terminates itself.
 // TODO(https://crbug.com/1066833): Make this a WebRunner test.
 TEST_F(CastRunnerIntegrationTest, OnTerminated_WindowClose) {
-  TestCastComponent component(cast_runner_.get());
-  const GURL url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  const GURL url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, url);
 
   component.CreateComponentContextAndStartComponent();
@@ -1208,8 +1059,8 @@
 // exit code ZX_OK if Kill() is used.
 // TODO(https://crbug.com/1066833): Make this a WebRunner test.
 TEST_F(CastRunnerIntegrationTest, OnTerminated_ComponentKill) {
-  TestCastComponent component(cast_runner_.get());
-  const GURL url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  const GURL url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, url);
 
   component.CreateComponentContextAndStartComponent();
@@ -1240,8 +1091,8 @@
 // Ensures that CastRunner handles the value not being specified.
 // TODO(https://crrev.com/c/2516246): Check for no logging.
 TEST_F(CastRunnerIntegrationTest, InitialMinConsoleLogSeverity_NotSet) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   auto app_config =
       FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
 
@@ -1255,8 +1106,8 @@
 
 // TODO(https://crrev.com/c/2516246): Check for logging.
 TEST_F(CastRunnerIntegrationTest, InitialMinConsoleLogSeverity_DEBUG) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   auto app_config =
       FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
 
@@ -1270,9 +1121,9 @@
 }
 
 TEST_F(CastRunnerIntegrationTest, WebGLContextAbsentWithoutVulkanFeature) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const char kTestPath[] = "/webgl_presence.html";
-  const GURL test_url = test_server_.GetURL(kTestPath);
+  const GURL test_url = test_server().GetURL(kTestPath);
   component.app_config_manager()->AddApp(kTestAppId, test_url);
 
   component.CreateComponentContextAndStartComponent();
@@ -1282,7 +1133,7 @@
 
 TEST_F(CastRunnerIntegrationTest,
        WebGLContextAbsentWithoutVulkanFeature_IsolatedRunner) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const GURL kContentDirectoryUrl("fuchsia-dir://testdata/webgl_presence.html");
 
   component.RegisterAppWithTestData(kContentDirectoryUrl);
@@ -1295,8 +1146,8 @@
 // Verifies that starting a component fails if CORS exempt headers cannot be
 // fetched.
 TEST_F(CastRunnerIntegrationTest, MissingCorsExemptHeaderProvider) {
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, app_url);
 
   // Start the Cast component, and wait for the controller to disconnect.
@@ -1330,8 +1181,8 @@
   EXPECT_TRUE(succeeded);
 
   // Verify that it is no longer possible to launch a component.
-  TestCastComponent component(cast_runner_.get());
-  GURL app_url = test_server_.GetURL(kBlankAppUrl);
+  TestCastComponent component(cast_runner());
+  GURL app_url = test_server().GetURL(kBlankAppUrl);
   component.app_config_manager()->AddApp(kTestAppId, app_url);
   component.StartCastComponent(base::StrCat({"cast:", kTestAppId}));
   component.ExpectControllerDisconnectWithStatus(ZX_ERR_PEER_CLOSED);
@@ -1340,7 +1191,7 @@
 // Verifies that CastRunner offers a chromium.cast.DataReset component, that
 // provides the DataReset service.
 TEST_F(CastRunnerIntegrationTest, DataReset_component) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   constexpr char kDataResetComponentName[] = "cast:chromium.cast.DataReset";
   component.StartCastComponent(kDataResetComponentName);
 
@@ -1361,86 +1212,17 @@
   EXPECT_TRUE(succeeded);
 }
 
-class CastRunnerCfv1ShimIntegrationTest : public CastRunnerIntegrationTest {
- public:
-  CastRunnerCfv1ShimIntegrationTest()
-      : CastRunnerIntegrationTest(kCastRunnerFeaturesCfv1Shim),
-        fake_runner_publisher_(
-            &services_for_cast_runner_,
-            [this](fidl::InterfaceRequest<fuchsia::sys::Runner> request) {
-              received_requests_.push_back(std::move(request));
-              if (on_request_received_)
-                on_request_received_.Run();
-            },
-            "fuchsia.sys.Runner-cast") {}
-
-  void RunUntilRequestsReceived(size_t expected_count) {
-    base::RunLoop run_loop;
-    base::AutoReset reset(&on_request_received_,
-                          base::BindLambdaForTesting([&]() {
-                            if (received_requests_.size() == expected_count)
-                              run_loop.Quit();
-                          }));
-    run_loop.Run();
-  }
-
- protected:
-  std::vector<fidl::InterfaceRequest<fuchsia::sys::Runner>> received_requests_;
-  base::RepeatingClosure on_request_received_;
-
- private:
-  base::ScopedServicePublisher<fuchsia::sys::Runner> fake_runner_publisher_;
-};
-
-// Ensure that when running in CFv1 "shim" mode, all connection attempts are
-// trivially redirected to a fuchsia.sys.Runner-cast service capability in
-// the shim Runner's environment.
-TEST_F(CastRunnerCfv1ShimIntegrationTest, ProxiesConnect) {
-  ASSERT_EQ(received_requests_.size(), 0u);
-
-  // The test constructor launched the CastRunner, configured as CFv1 shim,
-  // and immediately connected to it. That should result in two requests via the
-  // additional-services, which will be handled by |fake_runner_publisher_|
-  // as soon as the message loop is allowed to pump events.
-  // The first request is from the Runner shim itself, to allow it to monitor
-  // whether the service capability is still valid.
-  // The second is the test's connection to the shim Runner.
-  RunUntilRequestsReceived(2u);
-};
-
-// Ensure that CFv1 "shim" mode tears down the Runner component if the
-// underlying service capability disconnects it. This is required in order to
-// have the shim correctly reflect instability in the real Runner, to the CFv1
-// framework.
-TEST_F(CastRunnerCfv1ShimIntegrationTest, ExitOnFailure) {
-  // |cast_runner_| is expected to disconnect, so remove the error handler.
-  cast_runner_.set_error_handler([](zx_status_t) {});
-
-  // Wait for the two incoming Runner connections.
-  RunUntilRequestsReceived(2u);
-
-  // Close the two connections, and expect the Runner to self-terminate.
-  received_requests_.clear();
-  base::RunLoop loop;
-  cast_runner_controller_.ptr().set_error_handler(
-      [quit_loop = loop.QuitClosure()](zx_status_t status) {
-        EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
-        quit_loop.Run();
-      });
-  loop.Run();
-};
-
 class CastRunnerFrameHostIntegrationTest : public CastRunnerIntegrationTest {
  public:
   CastRunnerFrameHostIntegrationTest()
-      : CastRunnerIntegrationTest(kCastRunnerFeaturesFrameHost) {}
+      : CastRunnerIntegrationTest(test::kCastRunnerFeaturesFrameHost) {}
 };
 
 // Verifies that the CastRunner offers a fuchsia.web.FrameHost service.
 // TODO(crbug.com/1144102): Clean up config-data vs command-line flags handling
 // and add a not-enabled test here.
 TEST_F(CastRunnerFrameHostIntegrationTest, FrameHostComponent) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   constexpr char kFrameHostComponentName[] = "cast:fuchsia.web.FrameHost";
   component.StartCastComponent(kFrameHostComponentName);
 
@@ -1454,7 +1236,7 @@
   // Verify that a response is received for a LoadUrl() request to the frame.
   fuchsia::web::NavigationControllerPtr controller;
   frame->GetNavigationController(controller.NewRequest());
-  const GURL url = test_server_.GetURL(kBlankAppUrl);
+  const GURL url = test_server().GetURL(kBlankAppUrl);
   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
       controller.get(), fuchsia::web::LoadUrlParams(), url.spec()));
 }
@@ -1470,14 +1252,14 @@
 class MAYBE_VulkanCastRunnerIntegrationTest : public CastRunnerIntegrationTest {
  public:
   MAYBE_VulkanCastRunnerIntegrationTest()
-      : CastRunnerIntegrationTest(kCastRunnerFeaturesVulkan) {}
+      : CastRunnerIntegrationTest(test::kCastRunnerFeaturesVulkan) {}
 };
 
 TEST_F(MAYBE_VulkanCastRunnerIntegrationTest,
        WebGLContextPresentWithVulkanFeature) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const char kTestPath[] = "/webgl_presence.html";
-  const GURL test_url = test_server_.GetURL(kTestPath);
+  const GURL test_url = test_server().GetURL(kTestPath);
   component.app_config_manager()->AddApp(kTestAppId, test_url);
 
   component.CreateComponentContextAndStartComponent();
@@ -1487,7 +1269,7 @@
 
 TEST_F(MAYBE_VulkanCastRunnerIntegrationTest,
        WebGLContextPresentWithVulkanFeature_IsolatedRunner) {
-  TestCastComponent component(cast_runner_.get());
+  TestCastComponent component(cast_runner());
   const GURL kContentDirectoryUrl("fuchsia-dir://testdata/webgl_presence.html");
 
   component.RegisterAppWithTestData(kContentDirectoryUrl);
diff --git a/fuchsia_web/runners/cast/cast_runner_integration_test_base.cc b/fuchsia_web/runners/cast/cast_runner_integration_test_base.cc
new file mode 100644
index 0000000..9c099e4
--- /dev/null
+++ b/fuchsia_web/runners/cast/cast_runner_integration_test_base.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia_web/runners/cast/cast_runner_integration_test_base.h"
+
+#include <fuchsia/io/cpp/fidl.h>
+#include <fuchsia/sys/cpp/fidl.h>
+#include <lib/fdio/fd.h>
+#include <lib/sys/cpp/component_context.h>
+#include <unistd.h>
+#include <zircon/processargs.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
+#include "fuchsia_web/runners/cast/cast_runner_switches.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+CastRunnerIntegrationTest::CastRunnerIntegrationTest()
+    : CastRunnerIntegrationTest(test::kCastRunnerFeaturesNone) {}
+
+CastRunnerIntegrationTest::CastRunnerIntegrationTest(
+    test::CastRunnerFeatures runner_features)
+    : cast_runner_launcher_(runner_features) {}
+
+CastRunnerIntegrationTest::~CastRunnerIntegrationTest() = default;
+
+void CastRunnerIntegrationTest::SetUp() {
+  cast_runner_services_ = cast_runner_launcher_.StartCastRunner();
+
+  // Connect to the CastRunner's fuchsia.sys.Runner interface.
+  cast_runner_ = cast_runner_services().Connect<fuchsia::sys::Runner>();
+  cast_runner_.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status) << "CastRunner closed channel.";
+    ADD_FAILURE();
+  });
+
+  static constexpr base::StringPiece kTestServerRoot(
+      "fuchsia_web/runners/cast/testdata");
+  test_server_.ServeFilesFromSourceDirectory(kTestServerRoot);
+  net::test_server::RegisterDefaultHandlers(&test_server_);
+  ASSERT_TRUE(test_server_.Start());
+}
+
+void CastRunnerIntegrationTest::TearDown() {
+  // Unbind the Runner channel, to prevent it from triggering an error when
+  // the CastRunner and WebEngine are torn down.
+  cast_runner_.Unbind();
+}
diff --git a/fuchsia_web/runners/cast/cast_runner_integration_test_base.h b/fuchsia_web/runners/cast/cast_runner_integration_test_base.h
new file mode 100644
index 0000000..839940d
--- /dev/null
+++ b/fuchsia_web/runners/cast/cast_runner_integration_test_base.h
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_WEB_RUNNERS_CAST_CAST_RUNNER_INTEGRATION_TEST_BASE_H_
+#define FUCHSIA_WEB_RUNNERS_CAST_CAST_RUNNER_INTEGRATION_TEST_BASE_H_
+
+#include <fuchsia/sys/cpp/fidl.h>
+#include <lib/fidl/cpp/interface_request.h>
+#include <lib/sys/cpp/outgoing_directory.h>
+#include <lib/sys/cpp/service_directory.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/fuchsia/test_component_controller.h"
+#include "base/location.h"
+#include "base/test/scoped_run_loop_timeout.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "fuchsia_web/runners/cast/test/cast_runner_features.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_CFV1_LAUNCHER)
+#include "fuchsia_web/runners/cast/test/cast_runner_launcher_v1.h"  // nogncheck
+#endif
+
+// The base class for cast runner integration tests; templated on the type of
+// launcher used to start the component. This allows the same tests to be used
+// under both component framework v1 (using fuchsia.sys.Launcher) and v2
+// (using component_testing::RealmBuilder).
+class CastRunnerIntegrationTest : public testing::Test {
+ public:
+#if defined(USE_CFV1_LAUNCHER)
+  using Launcher = test::CastRunnerLauncherV1;
+#endif
+
+  CastRunnerIntegrationTest(const CastRunnerIntegrationTest&) = delete;
+  CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) =
+      delete;
+
+ protected:
+  // Convenience constructor with `runner_features` == kCastRunnerFeaturesNone.
+  CastRunnerIntegrationTest();
+  explicit CastRunnerIntegrationTest(test::CastRunnerFeatures runner_features);
+  ~CastRunnerIntegrationTest() override;
+
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+  Launcher& cast_runner_launcher() { return cast_runner_launcher_; }
+  net::EmbeddedTestServer& test_server() { return test_server_; }
+  fuchsia::sys::RunnerPtr& cast_runner() { return cast_runner_; }
+  const sys::ServiceDirectory& cast_runner_services() const {
+    return *cast_runner_services_;
+  }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+  net::EmbeddedTestServer test_server_;
+
+  // TODO(https://crbug.com/1168538): Override the RunLoop timeout set by
+  // |task_environment_| to allow for the very high variability in web.Context
+  // launch times.
+  const base::test::ScopedRunLoopTimeout scoped_timeout_{
+      FROM_HERE, TestTimeouts::action_max_timeout()};
+
+  Launcher cast_runner_launcher_;
+
+  fuchsia::sys::RunnerPtr cast_runner_;
+
+  std::unique_ptr<sys::ServiceDirectory> cast_runner_services_;
+};
+
+#endif  // FUCHSIA_WEB_RUNNERS_CAST_CAST_RUNNER_INTEGRATION_TEST_BASE_H_
diff --git a/fuchsia_web/runners/cast/cast_runner_integration_test_cfv1.cc b/fuchsia_web/runners/cast/cast_runner_integration_test_cfv1.cc
new file mode 100644
index 0000000..63181c1
--- /dev/null
+++ b/fuchsia_web/runners/cast/cast_runner_integration_test_cfv1.cc
@@ -0,0 +1,86 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/sys/cpp/fidl.h>
+#include <lib/fidl/cpp/interface_request.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/callback.h"
+#include "base/fuchsia/scoped_service_publisher.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "fuchsia_web/runners/cast/cast_runner_integration_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CastRunnerCfv1ShimIntegrationTest : public CastRunnerIntegrationTest {
+ public:
+  CastRunnerCfv1ShimIntegrationTest()
+      : CastRunnerIntegrationTest(test::kCastRunnerFeaturesCfv1Shim),
+        fake_runner_publisher_(
+            &cast_runner_launcher().services_for_runner(),
+            [this](fidl::InterfaceRequest<fuchsia::sys::Runner> request) {
+              received_requests_.push_back(std::move(request));
+              if (on_request_received_)
+                on_request_received_.Run();
+            },
+            "fuchsia.sys.Runner-cast") {}
+
+  void RunUntilRequestsReceived(size_t expected_count) {
+    base::RunLoop run_loop;
+    base::AutoReset reset(&on_request_received_,
+                          base::BindLambdaForTesting([&]() {
+                            if (received_requests_.size() == expected_count)
+                              run_loop.Quit();
+                          }));
+    run_loop.Run();
+  }
+
+ protected:
+  std::vector<fidl::InterfaceRequest<fuchsia::sys::Runner>> received_requests_;
+  base::RepeatingClosure on_request_received_;
+
+ private:
+  base::ScopedServicePublisher<fuchsia::sys::Runner> fake_runner_publisher_;
+};
+
+// Ensure that when running in CFv1 "shim" mode, all connection attempts are
+// trivially redirected to a fuchsia.sys.Runner-cast service capability in
+// the shim Runner's environment.
+TEST_F(CastRunnerCfv1ShimIntegrationTest, ProxiesConnect) {
+  ASSERT_EQ(received_requests_.size(), 0u);
+
+  // The test constructor launched the CastRunner, configured as CFv1 shim,
+  // and immediately connected to it. That should result in two requests via the
+  // additional-services, which will be handled by |fake_runner_publisher_|
+  // as soon as the message loop is allowed to pump events.
+  // The first request is from the Runner shim itself, to allow it to monitor
+  // whether the service capability is still valid.
+  // The second is the test's connection to the shim Runner.
+  RunUntilRequestsReceived(2u);
+};
+
+// Ensure that CFv1 "shim" mode tears down the Runner component if the
+// underlying service capability disconnects it. This is required in order to
+// have the shim correctly reflect instability in the real Runner, to the CFv1
+// framework.
+TEST_F(CastRunnerCfv1ShimIntegrationTest, ExitOnFailure) {
+  // |cast_runner_| is expected to disconnect, so remove the error handler.
+  cast_runner().set_error_handler([](zx_status_t) {});
+
+  // Wait for the two incoming Runner connections.
+  RunUntilRequestsReceived(2u);
+
+  // Close the two connections, and expect the Runner to self-terminate.
+  received_requests_.clear();
+  base::RunLoop loop;
+  cast_runner_launcher().controller_ptr().set_error_handler(
+      [quit_loop = loop.QuitClosure()](zx_status_t status) {
+        EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
+        quit_loop.Run();
+      });
+  loop.Run();
+};
diff --git a/fuchsia_web/runners/cast/test/BUILD.gn b/fuchsia_web/runners/cast/test/BUILD.gn
new file mode 100644
index 0000000..e24a4d0b
--- /dev/null
+++ b/fuchsia_web/runners/cast/test/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+config("cfv1_config") {
+  defines = [ "USE_CFV1_LAUNCHER" ]
+}
+
+source_set("cfv1_integration") {
+  testonly = true
+  public = [
+    "cast_runner_features.h",
+    "cast_runner_launcher_v1.h",
+  ]
+  sources = [ "cast_runner_launcher_v1.cc" ]
+  public_deps = [
+    "//base/test:test_support",
+    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sys",
+    "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+  ]
+  public_configs = [ ":cfv1_config" ]
+  deps = [
+    "//base",
+    "//fuchsia_web/runners:cast_runner_core",
+    "//media/fuchsia/audio:test_support",
+    "//testing/gtest",
+    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io",
+    "//third_party/fuchsia-sdk/sdk/pkg/fdio",
+  ]
+}
diff --git a/fuchsia_web/runners/cast/test/DEPS b/fuchsia_web/runners/cast/test/DEPS
new file mode 100644
index 0000000..3da756e2
--- /dev/null
+++ b/fuchsia_web/runners/cast/test/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+media/fuchsia/audio/fake_audio_device_enumerator.h",
+]
diff --git a/fuchsia_web/runners/cast/test/cast_runner_features.h b/fuchsia_web/runners/cast/test/cast_runner_features.h
new file mode 100644
index 0000000..a32ac50
--- /dev/null
+++ b/fuchsia_web/runners/cast/test/cast_runner_features.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_WEB_RUNNERS_CAST_TEST_CAST_RUNNER_FEATURES_H_
+#define FUCHSIA_WEB_RUNNERS_CAST_TEST_CAST_RUNNER_FEATURES_H_
+
+namespace test {
+
+// A bitfield of feature bits used by cast runner component test launchers.
+using CastRunnerFeatures = uint32_t;
+
+// Individual bitmasks for the CastRunnerFeatures bitfield.
+enum : uint32_t {
+  kCastRunnerFeaturesNone = 0U,
+  kCastRunnerFeaturesHeadless = 1U << 0,
+  kCastRunnerFeaturesVulkan = 1U << 1,
+  kCastRunnerFeaturesFrameHost = 1U << 2,
+  kCastRunnerFeaturesFakeAudioDeviceEnumerator = 1U << 3,
+#if defined(USE_CFV1_LAUNCHER)
+  kCastRunnerFeaturesCfv1Shim = 1U << 4,
+#endif
+};
+
+}  // namespace test
+
+#endif  // FUCHSIA_WEB_RUNNERS_CAST_TEST_CAST_RUNNER_FEATURES_H_
diff --git a/fuchsia_web/runners/cast/test/cast_runner_launcher_v1.cc b/fuchsia_web/runners/cast/test/cast_runner_launcher_v1.cc
new file mode 100644
index 0000000..e342e9fb
--- /dev/null
+++ b/fuchsia_web/runners/cast/test/cast_runner_launcher_v1.cc
@@ -0,0 +1,102 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia_web/runners/cast/test/cast_runner_launcher_v1.h"
+
+#include <fuchsia/io/cpp/fidl.h>
+#include <fuchsia/sys/cpp/fidl.h>
+#include <lib/fdio/fd.h>
+#include <lib/sys/cpp/component_context.h>
+#include <unistd.h>
+#include <zircon/processargs.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
+#include "fuchsia_web/runners/cast/cast_runner_switches.h"
+#include "media/fuchsia/audio/fake_audio_device_enumerator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kEnableFrameHostComponent[] = "enable-frame-host-component";
+
+constexpr char kEnableCfv1Shim[] = "enable-cfv1-shim";
+
+}  // namespace
+
+namespace test {
+
+CastRunnerLauncherV1::CastRunnerLauncherV1(CastRunnerFeatures runner_features)
+    : runner_features_(runner_features) {}
+
+CastRunnerLauncherV1::~CastRunnerLauncherV1() = default;
+
+std::unique_ptr<sys::ServiceDirectory> CastRunnerLauncherV1::StartCastRunner() {
+  fuchsia::sys::LaunchInfo launch_info;
+  launch_info.url =
+      "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx";
+
+  // Clone stderr from the current process to CastRunner and ask it to
+  // redirect all logs to stderr.
+  launch_info.err = fuchsia::sys::FileDescriptor::New();
+  launch_info.err->type0 = PA_FD;
+  zx_status_t status = fdio_fd_clone(
+      STDERR_FILENO, launch_info.err->handle0.reset_and_get_address());
+  ZX_CHECK(status == ZX_OK, status);
+
+  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+  command_line.AppendSwitchASCII("enable-logging", "stderr");
+
+  if (runner_features_ & kCastRunnerFeaturesHeadless)
+    command_line.AppendSwitch(kForceHeadlessForTestsSwitch);
+  if (!(runner_features_ & kCastRunnerFeaturesVulkan))
+    command_line.AppendSwitch(kDisableVulkanForTestsSwitch);
+  if (runner_features_ & kCastRunnerFeaturesFrameHost)
+    command_line.AppendSwitch(kEnableFrameHostComponent);
+  if (runner_features_ & kCastRunnerFeaturesCfv1Shim)
+    command_line.AppendSwitch(kEnableCfv1Shim);
+
+  // Add all switches and arguments, skipping the program.
+  launch_info.arguments.emplace(std::vector<std::string>(
+      command_line.argv().begin() + 1, command_line.argv().end()));
+
+  auto additional_services = std::make_unique<fuchsia::sys::ServiceList>();
+  auto* svc_dir = services_for_runner().GetOrCreateDirectory("svc");
+  if (runner_features_ & kCastRunnerFeaturesFakeAudioDeviceEnumerator) {
+    fake_audio_device_enumerator_ =
+        std::make_unique<media::FakeAudioDeviceEnumerator>(svc_dir);
+    additional_services->names.push_back(
+        fuchsia::media::AudioDeviceEnumerator::Name_);
+  }
+  if (runner_features_ & kCastRunnerFeaturesCfv1Shim) {
+    additional_services->names.push_back("fuchsia.sys.Runner-cast");
+  }
+
+  fuchsia::io::DirectoryHandle svc_dir_handle;
+  svc_dir->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
+                     fuchsia::io::OpenFlags::RIGHT_WRITABLE,
+                 svc_dir_handle.NewRequest().TakeChannel());
+  additional_services->host_directory = svc_dir_handle.TakeChannel();
+
+  launch_info.additional_services = std::move(additional_services);
+
+  fuchsia::io::DirectoryHandle cast_runner_services_dir;
+  launch_info.directory_request =
+      cast_runner_services_dir.NewRequest().TakeChannel();
+
+  fuchsia::sys::LauncherPtr launcher;
+  base::ComponentContextForProcess()->svc()->Connect(launcher.NewRequest());
+  launcher->CreateComponent(std::move(launch_info),
+                            controller_.ptr().NewRequest());
+  return std::make_unique<sys::ServiceDirectory>(
+      std::move(cast_runner_services_dir));
+}
+
+}  // namespace test
diff --git a/fuchsia_web/runners/cast/test/cast_runner_launcher_v1.h b/fuchsia_web/runners/cast/test/cast_runner_launcher_v1.h
new file mode 100644
index 0000000..9d4e51b5
--- /dev/null
+++ b/fuchsia_web/runners/cast/test/cast_runner_launcher_v1.h
@@ -0,0 +1,64 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_WEB_RUNNERS_CAST_TEST_CAST_RUNNER_LAUNCHER_V1_H_
+#define FUCHSIA_WEB_RUNNERS_CAST_TEST_CAST_RUNNER_LAUNCHER_V1_H_
+
+#include <fuchsia/sys/cpp/fidl.h>
+#include <lib/sys/cpp/outgoing_directory.h>
+#include <lib/sys/cpp/service_directory.h>
+
+#include <memory>
+
+#include "base/fuchsia/test_component_controller.h"
+#include "fuchsia_web/runners/cast/test/cast_runner_features.h"
+
+namespace media {
+class FakeAudioDeviceEnumerator;
+}
+
+namespace test {
+
+// A launcher for CastRunnerIntegrationTestBase that uses fuchsia.sys.Launcher
+// to start the cast runner component. This is for use with the CFv1 variant of
+// the integration tests.
+class CastRunnerLauncherV1 {
+ public:
+  // Creates an instance that will launch cast_runner.cmx with the features
+  // indicated in the `runner_features` bitmask. This can be used, for example,
+  // to provide a fake AudioDeviceEnumerator to the runner.
+  explicit CastRunnerLauncherV1(CastRunnerFeatures runner_features);
+  CastRunnerLauncherV1(const CastRunnerLauncherV1&) = delete;
+  CastRunnerLauncherV1& operator=(const CastRunnerLauncherV1&) = delete;
+  ~CastRunnerLauncherV1();
+
+  std::unique_ptr<sys::ServiceDirectory> StartCastRunner();
+
+  ::fuchsia::sys::ComponentControllerPtr& controller_ptr() {
+    return controller_.ptr();
+  }
+
+  // Returns the outgoing directory for services given to the runner. Services
+  // must be added before calling StartCastRunner().
+  sys::OutgoingDirectory& services_for_runner() { return services_for_runner_; }
+
+ private:
+  const CastRunnerFeatures runner_features_;
+
+  base::TestComponentController controller_;
+
+  // Directory used to publish test ContextProvider to CastRunner. Some tests
+  // restart ContextProvider, so we can't pass the services directory from
+  // ContextProvider to CastRunner directly.
+  sys::OutgoingDirectory services_for_runner_;
+
+  // A fake for fuchsia.media.AudioDeviceEnumerator that is provided to the
+  // runner upon request via `runner_features`.
+  std::unique_ptr<media::FakeAudioDeviceEnumerator>
+      fake_audio_device_enumerator_;
+};
+
+}  // namespace test
+
+#endif  // FUCHSIA_WEB_RUNNERS_CAST_TEST_CAST_RUNNER_LAUNCHER_V1_H_
diff --git a/fuchsia_web/runners/common/modular/agent_impl.cc b/fuchsia_web/runners/common/modular/agent_impl.cc
index 2fbcebf..f7623a4 100644
--- a/fuchsia_web/runners/common/modular/agent_impl.cc
+++ b/fuchsia_web/runners/common/modular/agent_impl.cc
@@ -4,9 +4,13 @@
 
 #include "fuchsia_web/runners/common/modular/agent_impl.h"
 
+#include <lib/fdio/directory.h>
 #include <lib/sys/cpp/component_context.h>
 
+#include <utility>
+
 #include "base/bind.h"
+#include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/process_context.h"
 
 namespace cr_fuchsia {
@@ -60,7 +64,21 @@
     : create_component_state_callback_(
           std::move(create_component_state_callback)),
       public_service_names_(std::move(public_service_names)),
-      agent_binding_(outgoing_directory, this) {}
+      agent_binding_(outgoing_directory, this) {
+  if (!public_service_names_.empty()) {
+    fuchsia::io::DirectoryHandle root_directory;
+    zx_status_t status =
+        outgoing_directory->Serve(root_directory.NewRequest().TakeChannel());
+    ZX_CHECK(status == ZX_OK, status) << "Serve(root)";
+    fuchsia::io::DirectoryHandle svc_directory;
+    status = fdio_service_connect_at(
+        root_directory.channel().get(), "svc",
+        svc_directory.NewRequest().TakeChannel().release());
+    ZX_CHECK(status == ZX_OK, status) << "open(svc)";
+    public_services_ =
+        std::make_unique<sys::ServiceDirectory>(std::move(svc_directory));
+  }
+}
 
 AgentImpl::~AgentImpl() {
   DCHECK(active_components_.empty());
@@ -81,10 +99,9 @@
     for (const auto& service_name : public_service_names_) {
       zx_status_t status = outgoing->AddPublicService(
           std::make_unique<vfs::Service>(
-              [service_name](zx::channel request,
-                             async_dispatcher_t* dispatcher) {
-                base::ComponentContextForProcess()->svc()->Connect(
-                    service_name, std::move(request));
+              [public_services = public_services_.get(), service_name](
+                  zx::channel request, async_dispatcher_t* dispatcher) {
+                public_services->Connect(service_name, std::move(request));
               }),
           service_name);
       CHECK_EQ(status, ZX_OK);
diff --git a/fuchsia_web/runners/common/modular/agent_impl.h b/fuchsia_web/runners/common/modular/agent_impl.h
index e5cee3e..6620414 100644
--- a/fuchsia_web/runners/common/modular/agent_impl.h
+++ b/fuchsia_web/runners/common/modular/agent_impl.h
@@ -9,6 +9,7 @@
 #include <lib/fidl/cpp/binding_set.h>
 #include <lib/sys/cpp/component_context.h>
 #include <lib/zx/channel.h>
+
 #include <memory>
 #include <string>
 #include <utility>
@@ -20,6 +21,10 @@
 #include "base/fuchsia/service_provider_impl.h"
 #include "base/strings/string_piece.h"
 
+namespace sys {
+class ServiceDirectory;
+}
+
 namespace cr_fuchsia {
 
 // AgentImpl allows the set of services published to each component to be
@@ -142,6 +147,9 @@
   // Set of service names to publish to all components.
   const std::vector<std::string> public_service_names_;
 
+  // Used to route requests for public services via the outgoing directory.
+  std::unique_ptr<sys::ServiceDirectory> public_services_;
+
   // Binds this Agent implementation into the |outgoing_directory|.
   base::ScopedServiceBinding<::fuchsia::modular::Agent> agent_binding_;
 
diff --git a/fuchsia_web/runners/common/modular/agent_impl_unittest.cc b/fuchsia_web/runners/common/modular/agent_impl_unittest.cc
index 47d98fa..30854bd 100644
--- a/fuchsia_web/runners/common/modular/agent_impl_unittest.cc
+++ b/fuchsia_web/runners/common/modular/agent_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <lib/sys/cpp/component_context.h>
 
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
 #include "base/fuchsia/test_component_context_for_process.h"
 #include "base/logging.h"
 #include "base/test/task_environment.h"
@@ -72,17 +73,8 @@
 };
 
 class AgentImplTest : public ::testing::Test {
- public:
-  AgentImplTest() {
-    fidl::InterfaceHandle<fuchsia::io::Directory> directory;
-    services_.GetOrCreateDirectory("svc")->Serve(
-        fuchsia::io::OpenFlags::RIGHT_READABLE |
-            fuchsia::io::OpenFlags::RIGHT_WRITABLE,
-        directory.NewRequest().TakeChannel());
-
-    services_client_ =
-        std::make_unique<sys::ServiceDirectory>(std::move(directory));
-  }
+ protected:
+  AgentImplTest() = default;
 
   AgentImplTest(const AgentImplTest&) = delete;
   AgentImplTest& operator=(const AgentImplTest&) = delete;
@@ -91,17 +83,18 @@
     EXPECT_FALSE(agent_impl_);
     if (public_services_.empty()) {
       agent_impl_ = std::make_unique<AgentImpl>(
-          &services_, base::BindRepeating(&AgentImplTest::OnComponentConnect,
-                                          base::Unretained(this)));
+          base::ComponentContextForProcess()->outgoing().get(),
+          base::BindRepeating(&AgentImplTest::OnComponentConnect,
+                              base::Unretained(this)));
     } else {
       agent_impl_ = std::make_unique<AgentImpl>(
-          &services_,
+          base::ComponentContextForProcess()->outgoing().get(),
           base::BindRepeating(&AgentImplTest::OnComponentConnect,
                               base::Unretained(this)),
           public_services_);
     }
     fuchsia::modular::AgentPtr agent;
-    services_client_->Connect(agent.NewRequest());
+    test_context_.published_services()->Connect(agent.NewRequest());
     return agent;
   }
 
@@ -113,7 +106,6 @@
     public_services_ = std::move(services);
   }
 
- protected:
   std::unique_ptr<AgentImpl::ComponentStateBase> OnComponentConnect(
       base::StringPiece component_id) {
     if (component_id == kNoServicesComponentId) {
@@ -134,8 +126,7 @@
 
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
-  sys::OutgoingDirectory services_;
-  std::unique_ptr<sys::ServiceDirectory> services_client_;
+  const base::TestComponentContextForProcess test_context_;
 
   std::unique_ptr<AgentImpl> agent_impl_;
 
@@ -169,7 +160,7 @@
 
   // Verify that the Agent service is no longer available.
   base::test::TestFuture<zx_status_t> client_disconnect_status2;
-  services_client_->Connect(agent.NewRequest());
+  test_context_.published_services()->Connect(agent.NewRequest());
   agent.set_error_handler(cr_fuchsia::CallbackToFitFunction(
       client_disconnect_status2.GetCallback()));
 
@@ -393,21 +384,28 @@
   }
 }
 
+class AgentImplTestWithPublicService : public AgentImplTest {
+ protected:
+  static constexpr char kServiceName[] = "base.testfidl.TestInterface-public";
+
+  AgentImplTestWithPublicService() : AgentImplTest() {
+    // Publish kServiceName to the outgoing services directory for the current
+    // process (scoped to this test by the TestComponentContextForProcess).
+    // AgentImpl should route requests for this "public" service via the
+    // outgoing services directory.
+    base::ComponentContextForProcess()
+        ->outgoing()
+        ->AddPublicService<base::testfidl::TestInterface>(
+            [](fidl::InterfaceRequest<base::testfidl::TestInterface> request) {
+              request.Close(ZX_OK);
+            },
+            kServiceName);
+  }
+};
+
 // Verify that the DefaultComponentState publishes the process' outgoing
 // service directory.
-TEST_F(AgentImplTest, PublicService) {
-  base::TestComponentContextForProcess test_context;
-
-  constexpr char kServiceName[] = "base.testfidl.TestInterface-public";
-
-  // Publish a dummy service for the DefaultComponentState to "see".
-  test_context.additional_services()
-      ->AddPublicService<base::testfidl::TestInterface>(
-          [](fidl::InterfaceRequest<base::testfidl::TestInterface> request) {
-            request.Close(ZX_OK);
-          },
-          kServiceName);
-
+TEST_F(AgentImplTestWithPublicService, PublicService) {
   // Configure the AgentImpl to provide the "public" service.
   set_public_services({kServiceName});
   fuchsia::modular::AgentPtr agent = CreateAgentAndConnect();
@@ -415,12 +413,14 @@
   // Connect to the ServiceProvider for the a dummy component.
   fuchsia::sys::ServiceProviderPtr component_services;
   agent->Connect(kAccumulatorComponentId1, component_services.NewRequest());
+
+  // Connect to the public service.
   base::testfidl::TestInterfacePtr test_interface;
   component_services->ConnectToService(
       kServiceName, test_interface.NewRequest().TakeChannel());
 
-  // Wait for the service connection to be closed, and verify that the epitaph
-  // is the expected value, rather than ZX_ERR_PEER_CLOSED.
+  // If we successfully connect then the service connection will be closed
+  // with ZX_OK, by the connection-handler implementation in the test base.
   base::test::TestFuture<zx_status_t> service_disconnect_status;
   test_interface.set_error_handler(cr_fuchsia::CallbackToFitFunction(
       service_disconnect_status.GetCallback()));
@@ -434,4 +434,35 @@
   base::RunLoop().RunUntilIdle();
 }
 
+// Verify that services published via the outgoing directory, but not as
+// public services, are not available.
+TEST_F(AgentImplTestWithPublicService, PublicServiceNotProvided) {
+  // Configure the AgentImpl to provide the "public" service.
+  set_public_services({});
+  fuchsia::modular::AgentPtr agent = CreateAgentAndConnect();
+
+  // Connect to the ServiceProvider for the a dummy component.
+  fuchsia::sys::ServiceProviderPtr component_services;
+  agent->Connect(kAccumulatorComponentId1, component_services.NewRequest());
+
+  // Connect to the public service.
+  base::testfidl::TestInterfacePtr test_interface;
+  component_services->ConnectToService(
+      kServiceName, test_interface.NewRequest().TakeChannel());
+
+  // If the service is not routed as "public" by the AgentImpl then the
+  // request should simply be dropped.
+  base::test::TestFuture<zx_status_t> service_disconnect_status;
+  test_interface.set_error_handler(cr_fuchsia::CallbackToFitFunction(
+      service_disconnect_status.GetCallback()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(service_disconnect_status.IsReady());
+  EXPECT_EQ(service_disconnect_status.Get(), ZX_ERR_PEER_CLOSED);
+
+  // Close the ServiceProvider channel and spin the MessageLoop to let the
+  // AgentImpl clean up the component state.
+  component_services = nullptr;
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace cr_fuchsia
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_texture_storage_image.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_texture_storage_image.txt
deleted file mode 100644
index 39d0bf99..0000000
--- a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_texture_storage_image.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-Name
-
-    CHROMIUM_texture_storage_image
-
-Name Strings
-
-    GL_CHROMIUM_texture_storage_image
-
-Contributors
-
-    Antoine Labour, Google
-    John Bauman, Google
-    Sunny Sachanandani, Google
-
-Contact
-
-    Sunny Sachanandani, Google (sunnyps 'at' google.com)
-
-Status
-
-    In Progress
-
-Version
-
-    Last Modified Date: September 29, 2017
-    Version:            1
-
-Number
-
-    OpenGL ES Extension #???
-
-Dependencies
-
-    This extension is written against the OpenGL ES 2.0 Specification.
-    This extension depends on EXT_texture_storage.
-
-Overview
-
-    This allows clients to specify alternative texture backings which support
-    scanout and can be used as hardware overlays.
-
-New Tokens
-
-    Accepted by the <bufferusage> parameter of glTexStorage2DImageCHROMIUM:
-    GL_SCANOUT_CHROMIUM 0x6000
-
-New Procedures and Functions
-
-    The command
-
-        void TexStorage2DImageCHROMIUM(enum target, enum internalformat,
-                                       enum bufferusage, sizei width,
-                                       sizei height);
-
-    specifies that the (single) level of the texture is backed by a platform
-    image buffer with the specified usage. The only supported buffer usage is
-    SCANOUT_CHROMIUM which allows the texture to be used for scanout and in
-    hardware overlays. The supported internal formats are RGBA8_OES,
-    BGRA8_EXT, RGBAF16_EXT, and R8_EXT.
-
-Errors
-
-    A context lost will result when this call fails, either because of an
-    invalid parameter value or because of a runtime error such as an out of
-    memory condition.
-
-Revision History
-
-    Rev.    Date      Author     Changes
-    ----  ----------- ---------  ----------------------------------------
-      1   19 Jun 2017 jbauman    Initial revision (as CHROMIUM_texture_buffer)
-      2   06 Oct 2017 sunnyps    Rewritten as CHROMIUM_texture_storage_image
-
-
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index 0caec5d..3051102f 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -354,7 +354,6 @@
   GLES2_GET_FUN(UnlockDiscardableTextureCHROMIUM)
 #define glLockDiscardableTextureCHROMIUM \
   GLES2_GET_FUN(LockDiscardableTextureCHROMIUM)
-#define glTexStorage2DImageCHROMIUM GLES2_GET_FUN(TexStorage2DImageCHROMIUM)
 #define glSetColorSpaceMetadataCHROMIUM \
   GLES2_GET_FUN(SetColorSpaceMetadataCHROMIUM)
 #define glWindowRectanglesEXT GLES2_GET_FUN(WindowRectanglesEXT)
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index 43ad7a35e..5d5c5108 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -701,23 +701,6 @@
 #define GL_TEXTURE_FILTERING_HINT_CHROMIUM 0x8AF0
 #endif /* GL_CHROMIUM_texture_filtering_hint */
 
-#ifndef GL_CHROMIUM_texture_storage_image
-#define GL_CHROMIUM_texture_storage_image 1
-#ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL void GL_APIENTRY glTexStorage2DImageCHROMIUM(GLenum target,
-                                                        GLenum internalformat,
-                                                        GLenum bufferusage,
-                                                        GLsizei width,
-                                                        GLsizei height);
-#endif
-typedef void(GL_APIENTRYP PFNGLTEXSTORAGE2DIMAGECHROMIUM)(GLenum target,
-                                                          GLenum internalformat,
-                                                          GLenum bufferusage,
-                                                          GLsizei width,
-                                                          GLsizei height);
-#define GL_SCANOUT_CHROMIUM 0x6000
-#endif /* GL_CHROMIUM_texture_storage_image */
-
 #ifndef GL_CHROMIUM_color_space_metadata
 #define GL_CHROMIUM_color_space_metadata 1
 #ifdef GL_GLEXT_PROTOTYPES
diff --git a/gpu/command_buffer/build_cmd_buffer_lib.py b/gpu/command_buffer/build_cmd_buffer_lib.py
index 84deb2e8..60242b37 100644
--- a/gpu/command_buffer/build_cmd_buffer_lib.py
+++ b/gpu/command_buffer/build_cmd_buffer_lib.py
@@ -6394,7 +6394,7 @@
     with CHeaderWriter(filename, self.year) as f:
       # Forward declaration of a few enums used in constant argument
       # to avoid including GL header files.
-      enum_defines = {'GL_SCANOUT_CHROMIUM': '0x6000'}
+      enum_defines = {}
       if 'FenceSync' in self.function_info:
         enum_defines['GL_SYNC_GPU_COMMANDS_COMPLETE'] = '0x9117'
       if 'ClientWaitSync' in self.function_info:
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 1040ec6e..21ff785c 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1581,16 +1581,6 @@
       'GL_SYNC_FENCE',
     ],
   },
-  'ClientBufferUsage': {
-    'type': 'GLenum',
-    'is_complete': True,
-    'valid': [
-      'GL_SCANOUT_CHROMIUM',
-    ],
-    'invalid': [
-      'GL_NONE',
-    ],
-  },
   'WindowRectanglesMode': {
     'type': 'GLenum',
     'is_complete': True,
@@ -3949,12 +3939,6 @@
     'client_test': False,
     'extension': True,
   },
-  'TexStorage2DImageCHROMIUM': {
-    'decoder_func': 'DoTexStorage2DImageCHROMIUM',
-    'unit_test': False,
-    'extension': 'CHROMIUM_texture_storage_image',
-    'extension_flag': 'chromium_texture_storage_image',
-  },
   'SetColorSpaceMetadataCHROMIUM': {
     'type': 'Custom',
     'impl_func': False,
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 60a95b4..161077a 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1612,14 +1612,6 @@
 bool GL_APIENTRY GLES2LockDiscardableTextureCHROMIUM(GLuint texture_id) {
   return gles2::GetGLContext()->LockDiscardableTextureCHROMIUM(texture_id);
 }
-void GL_APIENTRY GLES2TexStorage2DImageCHROMIUM(GLenum target,
-                                                GLenum internalFormat,
-                                                GLenum bufferUsage,
-                                                GLsizei width,
-                                                GLsizei height) {
-  gles2::GetGLContext()->TexStorage2DImageCHROMIUM(target, internalFormat,
-                                                   bufferUsage, width, height);
-}
 void GL_APIENTRY GLES2SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
                                                     GLcolorSpace color_space) {
   gles2::GetGLContext()->SetColorSpaceMetadataCHROMIUM(texture_id, color_space);
@@ -3014,10 +3006,6 @@
             glLockDiscardableTextureCHROMIUM),
     },
     {
-        "glTexStorage2DImageCHROMIUM",
-        reinterpret_cast<GLES2FunctionPointer>(glTexStorage2DImageCHROMIUM),
-    },
-    {
         "glSetColorSpaceMetadataCHROMIUM",
         reinterpret_cast<GLES2FunctionPointer>(glSetColorSpaceMetadataCHROMIUM),
     },
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index 4a5fbd8f..14ab09a 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -3075,17 +3075,6 @@
   }
 }
 
-void TexStorage2DImageCHROMIUM(GLenum target,
-                               GLenum internalFormat,
-                               GLsizei width,
-                               GLsizei height) {
-  gles2::cmds::TexStorage2DImageCHROMIUM* c =
-      GetCmdSpace<gles2::cmds::TexStorage2DImageCHROMIUM>();
-  if (c) {
-    c->Init(target, internalFormat, width, height);
-  }
-}
-
 void SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
                                    GLuint shm_id,
                                    GLuint shm_offset,
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index c45173bc8..72cba99e 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -1136,12 +1136,6 @@
 
 bool LockDiscardableTextureCHROMIUM(GLuint texture_id) override;
 
-void TexStorage2DImageCHROMIUM(GLenum target,
-                               GLenum internalFormat,
-                               GLenum bufferUsage,
-                               GLsizei width,
-                               GLsizei height) override;
-
 void SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
                                    GLcolorSpace color_space) override;
 
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
index 1a1a0cd..5aec737 100644
--- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -3415,35 +3415,6 @@
   CheckGLError();
 }
 
-void GLES2Implementation::TexStorage2DImageCHROMIUM(GLenum target,
-                                                    GLenum internalFormat,
-                                                    GLenum bufferUsage,
-                                                    GLsizei width,
-                                                    GLsizei height) {
-  GPU_CLIENT_SINGLE_THREAD_CHECK();
-  GPU_CLIENT_LOG(
-      "[" << GetLogPrefix() << "] glTexStorage2DImageCHROMIUM("
-          << GLES2Util::GetStringTextureBindTarget(target) << ", "
-          << GLES2Util::GetStringTextureInternalFormatStorage(internalFormat)
-          << ", " << GLES2Util::GetStringClientBufferUsage(bufferUsage) << ", "
-          << width << ", " << height << ")");
-  if (bufferUsage != GL_SCANOUT_CHROMIUM) {
-    SetGLError(GL_INVALID_ENUM, "glTexStorage2DImageCHROMIUM",
-               "bufferUsage GL_INVALID_ENUM");
-    return;
-  }
-  if (width < 0) {
-    SetGLError(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM", "width < 0");
-    return;
-  }
-  if (height < 0) {
-    SetGLError(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM", "height < 0");
-    return;
-  }
-  helper_->TexStorage2DImageCHROMIUM(target, internalFormat, width, height);
-  CheckGLError();
-}
-
 void GLES2Implementation::WindowRectanglesEXT(GLenum mode,
                                               GLsizei count,
                                               const GLint* box) {
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index 67c2380..71afb60 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -2955,24 +2955,6 @@
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
 
-TEST_F(GLES2ImplementationTest, TexStorage2DImageCHROMIUM) {
-  struct Cmds {
-    cmds::TexStorage2DImageCHROMIUM cmd;
-  };
-  Cmds expected;
-  expected.cmd.Init(GL_TEXTURE_2D, GL_RGB565, 4, 5);
-
-  gl_->TexStorage2DImageCHROMIUM(GL_TEXTURE_2D, GL_RGB565, GL_SCANOUT_CHROMIUM,
-                                 4, 5);
-  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
-}
-
-TEST_F(GLES2ImplementationTest, TexStorage2DImageCHROMIUMInvalidConstantArg2) {
-  gl_->TexStorage2DImageCHROMIUM(GL_TEXTURE_2D, GL_RGB565, GL_NONE, 4, 5);
-  EXPECT_TRUE(NoCommandsWritten());
-  EXPECT_EQ(GL_INVALID_ENUM, CheckError());
-}
-
 TEST_F(GLES2ImplementationTest, WindowRectanglesEXT) {
   GLint data[2][4] = {{0}};
   struct Cmds {
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 9911765..f963eca 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -841,11 +841,6 @@
 virtual void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
 virtual void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
 virtual bool LockDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
-virtual void TexStorage2DImageCHROMIUM(GLenum target,
-                                       GLenum internalFormat,
-                                       GLenum bufferUsage,
-                                       GLsizei width,
-                                       GLsizei height) = 0;
 virtual void SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
                                            GLcolorSpace color_space) = 0;
 virtual void WindowRectanglesEXT(GLenum mode,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index 3709ac25..f2df4f1c 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -817,11 +817,6 @@
 void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) override;
 void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) override;
 bool LockDiscardableTextureCHROMIUM(GLuint texture_id) override;
-void TexStorage2DImageCHROMIUM(GLenum target,
-                               GLenum internalFormat,
-                               GLenum bufferUsage,
-                               GLsizei width,
-                               GLsizei height) override;
 void SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
                                    GLcolorSpace color_space) override;
 void WindowRectanglesEXT(GLenum mode, GLsizei count, const GLint* box) override;
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index 67fddeb..c02dd5b 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -1100,11 +1100,6 @@
     GLuint /* texture_id */) {
   return 0;
 }
-void GLES2InterfaceStub::TexStorage2DImageCHROMIUM(GLenum /* target */,
-                                                   GLenum /* internalFormat */,
-                                                   GLenum /* bufferUsage */,
-                                                   GLsizei /* width */,
-                                                   GLsizei /* height */) {}
 void GLES2InterfaceStub::SetColorSpaceMetadataCHROMIUM(
     GLuint /* texture_id */,
     GLcolorSpace /* color_space */) {}
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 09a446b..685dace 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -817,11 +817,6 @@
 void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) override;
 void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) override;
 bool LockDiscardableTextureCHROMIUM(GLuint texture_id) override;
-void TexStorage2DImageCHROMIUM(GLenum target,
-                               GLenum internalFormat,
-                               GLenum bufferUsage,
-                               GLsizei width,
-                               GLsizei height) override;
 void SetColorSpaceMetadataCHROMIUM(GLuint texture_id,
                                    GLcolorSpace color_space) override;
 void WindowRectanglesEXT(GLenum mode, GLsizei count, const GLint* box) override;
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 5b0f557..277bacb 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2335,16 +2335,6 @@
   return gl_->LockDiscardableTextureCHROMIUM(texture_id);
 }
 
-void GLES2TraceImplementation::TexStorage2DImageCHROMIUM(GLenum target,
-                                                         GLenum internalFormat,
-                                                         GLenum bufferUsage,
-                                                         GLsizei width,
-                                                         GLsizei height) {
-  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::TexStorage2DImageCHROMIUM");
-  gl_->TexStorage2DImageCHROMIUM(target, internalFormat, bufferUsage, width,
-                                 height);
-}
-
 void GLES2TraceImplementation::SetColorSpaceMetadataCHROMIUM(
     GLuint texture_id,
     GLcolorSpace color_space) {
diff --git a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
index eec0fd5..9fc5da5 100644
--- a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
@@ -128,12 +128,6 @@
                     GLenum internalFormat,
                     GLsizei width,
                     GLsizei height));
-  MOCK_METHOD5(TexStorage2DImageCHROMIUM,
-               void(GLenum target,
-                    GLenum internalFormat,
-                    GLenum bufferUsage,
-                    GLsizei width,
-                    GLsizei height));
 
   MOCK_METHOD2(PixelStorei, void(GLenum pname, GLint param));
   MOCK_METHOD2(TraceBeginCHROMIUM,
diff --git a/gpu/command_buffer/common/capabilities.h b/gpu/command_buffer/common/capabilities.h
index 755bd297..0329162 100644
--- a/gpu/command_buffer/common/capabilities.h
+++ b/gpu/command_buffer/common/capabilities.h
@@ -78,6 +78,7 @@
   int max_fragment_uniform_vectors = 0;
   int max_renderbuffer_size = 0;
   int max_texture_image_units = 0;
+  // Note this may be smaller than GL_MAX_TEXTURE_SIZE for a GLES context.
   int max_texture_size = 0;
   int max_varying_vectors = 0;
   int max_vertex_attribs = 0;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index 616b8e5..1c916a87 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -11,7 +11,6 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
 
-#define GL_SCANOUT_CHROMIUM 0x6000
 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
 #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
 
@@ -15186,60 +15185,6 @@
     offsetof(LockDiscardableTextureCHROMIUM, texture_id) == 4,
     "offset of LockDiscardableTextureCHROMIUM texture_id should be 4");
 
-struct TexStorage2DImageCHROMIUM {
-  typedef TexStorage2DImageCHROMIUM ValueType;
-  static const CommandId kCmdId = kTexStorage2DImageCHROMIUM;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLenum _target,
-            GLenum _internalFormat,
-            GLsizei _width,
-            GLsizei _height) {
-    SetHeader();
-    target = _target;
-    internalFormat = _internalFormat;
-    width = _width;
-    height = _height;
-  }
-
-  void* Set(void* cmd,
-            GLenum _target,
-            GLenum _internalFormat,
-            GLsizei _width,
-            GLsizei _height) {
-    static_cast<ValueType*>(cmd)->Init(_target, _internalFormat, _width,
-                                       _height);
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t target;
-  uint32_t internalFormat;
-  int32_t width;
-  int32_t height;
-  static const uint32_t bufferUsage = GL_SCANOUT_CHROMIUM;
-};
-
-static_assert(sizeof(TexStorage2DImageCHROMIUM) == 20,
-              "size of TexStorage2DImageCHROMIUM should be 20");
-static_assert(offsetof(TexStorage2DImageCHROMIUM, header) == 0,
-              "offset of TexStorage2DImageCHROMIUM header should be 0");
-static_assert(offsetof(TexStorage2DImageCHROMIUM, target) == 4,
-              "offset of TexStorage2DImageCHROMIUM target should be 4");
-static_assert(offsetof(TexStorage2DImageCHROMIUM, internalFormat) == 8,
-              "offset of TexStorage2DImageCHROMIUM internalFormat should be 8");
-static_assert(offsetof(TexStorage2DImageCHROMIUM, width) == 12,
-              "offset of TexStorage2DImageCHROMIUM width should be 12");
-static_assert(offsetof(TexStorage2DImageCHROMIUM, height) == 16,
-              "offset of TexStorage2DImageCHROMIUM height should be 16");
-
 struct SetColorSpaceMetadataCHROMIUM {
   typedef SetColorSpaceMetadataCHROMIUM ValueType;
   static const CommandId kCmdId = kSetColorSpaceMetadataCHROMIUM;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index 4399378c..c81c8d1 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -5015,22 +5015,6 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
-TEST_F(GLES2FormatTest, TexStorage2DImageCHROMIUM) {
-  cmds::TexStorage2DImageCHROMIUM& cmd =
-      *GetBufferAs<cmds::TexStorage2DImageCHROMIUM>();
-  void* next_cmd =
-      cmd.Set(&cmd, static_cast<GLenum>(11), static_cast<GLenum>(12),
-              static_cast<GLsizei>(13), static_cast<GLsizei>(14));
-  EXPECT_EQ(static_cast<uint32_t>(cmds::TexStorage2DImageCHROMIUM::kCmdId),
-            cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLenum>(11), cmd.target);
-  EXPECT_EQ(static_cast<GLenum>(12), cmd.internalFormat);
-  EXPECT_EQ(static_cast<GLsizei>(13), cmd.width);
-  EXPECT_EQ(static_cast<GLsizei>(14), cmd.height);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
 TEST_F(GLES2FormatTest, SetColorSpaceMetadataCHROMIUM) {
   cmds::SetColorSpaceMetadataCHROMIUM& cmd =
       *GetBufferAs<cmds::SetColorSpaceMetadataCHROMIUM>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index 78652ac..7a569f0c 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -318,28 +318,27 @@
   OP(InitializeDiscardableTextureCHROMIUM)                     /* 559 */ \
   OP(UnlockDiscardableTextureCHROMIUM)                         /* 560 */ \
   OP(LockDiscardableTextureCHROMIUM)                           /* 561 */ \
-  OP(TexStorage2DImageCHROMIUM)                                /* 562 */ \
-  OP(SetColorSpaceMetadataCHROMIUM)                            /* 563 */ \
-  OP(WindowRectanglesEXTImmediate)                             /* 564 */ \
-  OP(CreateGpuFenceINTERNAL)                                   /* 565 */ \
-  OP(WaitGpuFenceCHROMIUM)                                     /* 566 */ \
-  OP(DestroyGpuFenceCHROMIUM)                                  /* 567 */ \
-  OP(SetReadbackBufferShadowAllocationINTERNAL)                /* 568 */ \
-  OP(FramebufferTextureMultiviewOVR)                           /* 569 */ \
-  OP(MaxShaderCompilerThreadsKHR)                              /* 570 */ \
-  OP(CreateAndTexStorage2DSharedImageINTERNALImmediate)        /* 571 */ \
-  OP(BeginSharedImageAccessDirectCHROMIUM)                     /* 572 */ \
-  OP(EndSharedImageAccessDirectCHROMIUM)                       /* 573 */ \
-  OP(BeginBatchReadAccessSharedImageCHROMIUM)                  /* 574 */ \
-  OP(EndBatchReadAccessSharedImageCHROMIUM)                    /* 575 */ \
-  OP(EnableiOES)                                               /* 576 */ \
-  OP(DisableiOES)                                              /* 577 */ \
-  OP(BlendEquationiOES)                                        /* 578 */ \
-  OP(BlendEquationSeparateiOES)                                /* 579 */ \
-  OP(BlendFunciOES)                                            /* 580 */ \
-  OP(BlendFuncSeparateiOES)                                    /* 581 */ \
-  OP(ColorMaskiOES)                                            /* 582 */ \
-  OP(IsEnablediOES)                                            /* 583 */
+  OP(SetColorSpaceMetadataCHROMIUM)                            /* 562 */ \
+  OP(WindowRectanglesEXTImmediate)                             /* 563 */ \
+  OP(CreateGpuFenceINTERNAL)                                   /* 564 */ \
+  OP(WaitGpuFenceCHROMIUM)                                     /* 565 */ \
+  OP(DestroyGpuFenceCHROMIUM)                                  /* 566 */ \
+  OP(SetReadbackBufferShadowAllocationINTERNAL)                /* 567 */ \
+  OP(FramebufferTextureMultiviewOVR)                           /* 568 */ \
+  OP(MaxShaderCompilerThreadsKHR)                              /* 569 */ \
+  OP(CreateAndTexStorage2DSharedImageINTERNALImmediate)        /* 570 */ \
+  OP(BeginSharedImageAccessDirectCHROMIUM)                     /* 571 */ \
+  OP(EndSharedImageAccessDirectCHROMIUM)                       /* 572 */ \
+  OP(BeginBatchReadAccessSharedImageCHROMIUM)                  /* 573 */ \
+  OP(EndBatchReadAccessSharedImageCHROMIUM)                    /* 574 */ \
+  OP(EnableiOES)                                               /* 575 */ \
+  OP(DisableiOES)                                              /* 576 */ \
+  OP(BlendEquationiOES)                                        /* 577 */ \
+  OP(BlendEquationSeparateiOES)                                /* 578 */ \
+  OP(BlendFunciOES)                                            /* 579 */ \
+  OP(BlendFuncSeparateiOES)                                    /* 580 */ \
+  OP(ColorMaskiOES)                                            /* 581 */ \
+  OP(IsEnablediOES)                                            /* 582 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
index 523450a..bff7c45d 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
@@ -25,7 +25,6 @@
 static std::string GetStringBufferiv(uint32_t value);
 static std::string GetStringBufferuiv(uint32_t value);
 static std::string GetStringCapability(uint32_t value);
-static std::string GetStringClientBufferUsage(uint32_t value);
 static std::string GetStringCmpFunction(uint32_t value);
 static std::string GetStringCompressedTextureFormat(uint32_t value);
 static std::string GetStringCoverageModulationComponents(uint32_t value);
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index ba4a66aff..8e37089 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -965,10 +965,6 @@
         "GL_MULTISAMPLE_BUFFER_BIT6_QCOM",
     },
     {
-        0x6000,
-        "GL_SCANOUT_CHROMIUM",
-    },
-    {
         0x6003,
         "GL_GET_ERROR_QUERY_CHROMIUM",
     },
@@ -6997,14 +6993,6 @@
                                            std::size(string_table), value);
 }
 
-std::string GLES2Util::GetStringClientBufferUsage(uint32_t value) {
-  static const EnumToString string_table[] = {
-      {GL_SCANOUT_CHROMIUM, "GL_SCANOUT_CHROMIUM"},
-  };
-  return GLES2Util::GetQualifiedEnumString(string_table,
-                                           std::size(string_table), value);
-}
-
 std::string GLES2Util::GetStringCmpFunction(uint32_t value) {
   static const EnumToString string_table[] = {
       {GL_NEVER, "GL_NEVER"},     {GL_LESS, "GL_LESS"},
diff --git a/gpu/command_buffer/common/raster_cmd_format_autogen.h b/gpu/command_buffer/common/raster_cmd_format_autogen.h
index ccc53daf..41a4b8e6 100644
--- a/gpu/command_buffer/common/raster_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_autogen.h
@@ -11,8 +11,6 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_FORMAT_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_COMMON_RASTER_CMD_FORMAT_AUTOGEN_H_
 
-#define GL_SCANOUT_CHROMIUM 0x6000
-
 struct Finish {
   typedef Finish ValueType;
   static const CommandId kCmdId = kFinish;
diff --git a/gpu/command_buffer/common/webgpu_cmd_format_autogen.h b/gpu/command_buffer/common/webgpu_cmd_format_autogen.h
index 4d89ff5e..bf63974 100644
--- a/gpu/command_buffer/common/webgpu_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/webgpu_cmd_format_autogen.h
@@ -11,8 +11,6 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_WEBGPU_CMD_FORMAT_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_COMMON_WEBGPU_CMD_FORMAT_AUTOGEN_H_
 
-#define GL_SCANOUT_CHROMIUM 0x6000
-
 struct DawnCommands {
   typedef DawnCommands ValueType;
   static const CommandId kCmdId = kDawnCommands;
diff --git a/gpu/command_buffer/gles2_cmd_buffer_functions.txt b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
index 4633122..5ee9e3c 100644
--- a/gpu/command_buffer/gles2_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
@@ -355,9 +355,6 @@
 GL_APICALL void         GL_APIENTRY glUnlockDiscardableTextureCHROMIUM (GLuint texture_id);
 GL_APICALL bool         GL_APIENTRY glLockDiscardableTextureCHROMIUM (GLuint texture_id);
 
-// Extension CHROMIUM_texture_storage_image
-GL_APICALL void         GL_APIENTRY glTexStorage2DImageCHROMIUM (GLenumTextureBindTarget target, GLenumTextureInternalFormatStorage internalFormat, GLenumClientBufferUsage bufferUsage, GLsizei width, GLsizei height);
-
 // Extension CHROMIUM_color_space_metadata
 GL_APICALL void         GL_APIENTRY glSetColorSpaceMetadataCHROMIUM (GLuint texture_id, GLcolorSpace color_space);
 
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 639348e..40e86c2 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -361,14 +361,6 @@
       "//ui/base:features",
       "//ui/ozone",
     ]
-
-    # Adding this dependency for collecting some metrics checking GL
-    # extensions used on Ozone platforms. Will be removed once the
-    # metrics are collected (in 2-3 months hopefully).
-    # Ref bug: https://crbug.com/1310028
-    if (ozone_platform_x11) {
-      deps += [ "//ui/gfx/x" ]
-    }
   }
 
   if (enable_vulkan) {
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index 52f5f5c..6a2cf3bb 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -376,15 +376,17 @@
                     : gpu::ContextResult::kFatalFailure;
   }
 
-  if (feature_info_->workarounds().client_max_texture_size) {
-    max_texture_size = std::min(
-        max_texture_size, feature_info_->workarounds().client_max_texture_size);
+  if (feature_info_->workarounds().webgl_or_caps_max_texture_size &&
+      feature_info_->IsWebGLContext()) {
+    max_texture_size =
+        std::min(max_texture_size,
+                 feature_info_->workarounds().webgl_or_caps_max_texture_size);
     max_rectangle_texture_size =
         std::min(max_rectangle_texture_size,
-                 feature_info_->workarounds().client_max_texture_size);
+                 feature_info_->workarounds().webgl_or_caps_max_texture_size);
     max_cube_map_texture_size =
         std::min(max_cube_map_texture_size,
-                 feature_info_->workarounds().client_max_texture_size);
+                 feature_info_->workarounds().webgl_or_caps_max_texture_size);
   }
 
   if (feature_info_->workarounds().max_3d_array_texture_size) {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index e18c63c..00d3e1e 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -312,10 +312,9 @@
 #endif  // BUILDFLAG(IS_MAC)
 }
 
-void FeatureInfo::EnableCHROMIUMTextureStorageImage() {
-  if (!feature_flags_.chromium_texture_storage_image) {
-    feature_flags_.chromium_texture_storage_image = true;
-    AddExtensionString("GL_CHROMIUM_texture_storage_image");
+void FeatureInfo::EnableTextureStorageImage() {
+  if (!feature_flags_.texture_storage_image) {
+    feature_flags_.texture_storage_image = true;
   }
 }
 
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index 3d45112..96554ce4 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -128,7 +128,7 @@
     bool oes_rgb8_rgba8 = false;
     bool angle_robust_resource_initialization = false;
     bool nv_fence = false;
-    bool chromium_texture_storage_image = false;
+    bool texture_storage_image = false;
     bool ext_window_rectangles = false;
     bool chromium_gpu_fence = false;
     bool separate_stencil_ref_mask_writemask = false;
@@ -208,7 +208,7 @@
   bool IsWebGL2OrES3OrHigherContext() const;
   bool IsES31ForTestingContext() const;
 
-  void EnableCHROMIUMTextureStorageImage();
+  void EnableTextureStorageImage();
   void EnableCHROMIUMColorBufferFloatRGBA();
   void EnableCHROMIUMColorBufferFloatRGB();
   void EnableEXTFloatBlend();
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index f1ffb37..980907c 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -213,7 +213,7 @@
 #define GPU_OP(type, name) EXPECT_FALSE(info_->workarounds().name);
   GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP)
 #undef GPU_OP
-  EXPECT_EQ(0, info_->workarounds().client_max_texture_size);
+  EXPECT_EQ(0, info_->workarounds().webgl_or_caps_max_texture_size);
   EXPECT_FALSE(info_->workarounds().gl_clear_broken);
 }
 
@@ -1552,11 +1552,11 @@
 TEST_P(FeatureInfoTest, ParseDriverBugWorkaroundsMultiple) {
   gpu::GpuDriverBugWorkarounds workarounds;
   workarounds.exit_on_context_lost = true;
-  workarounds.client_max_texture_size = 4096;
+  workarounds.webgl_or_caps_max_texture_size = 4096;
   // Workarounds should get parsed without the need for a context.
   SetupWithWorkarounds(workarounds);
   EXPECT_TRUE(info_->workarounds().exit_on_context_lost);
-  EXPECT_EQ(4096, info_->workarounds().client_max_texture_size);
+  EXPECT_EQ(4096, info_->workarounds().webgl_or_caps_max_texture_size);
 }
 
 TEST_P(FeatureInfoTest, InitializeWithARBSync) {
diff --git a/gpu/command_buffer/service/gl_utils.cc b/gpu/command_buffer/service/gl_utils.cc
index 23313e9..beca4b5 100644
--- a/gpu/command_buffer/service/gl_utils.cc
+++ b/gpu/command_buffer/service/gl_utils.cc
@@ -1205,16 +1205,6 @@
   }
 }
 
-bool GetGFXBufferUsage(GLenum buffer_usage, gfx::BufferUsage* out_usage) {
-  switch (buffer_usage) {
-    case GL_SCANOUT_CHROMIUM:
-      *out_usage = gfx::BufferUsage::SCANOUT;
-      return true;
-    default:
-      return false;
-  }
-}
-
 bool IsASTCFormat(GLenum internal_format) {
   switch (internal_format) {
     case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
diff --git a/gpu/command_buffer/service/gl_utils.h b/gpu/command_buffer/service/gl_utils.h
index 27eae091..f8fd890 100644
--- a/gpu/command_buffer/service/gl_utils.h
+++ b/gpu/command_buffer/service/gl_utils.h
@@ -158,7 +158,6 @@
 gfx::OverlayTransform GetGFXOverlayTransform(GLenum plane_transform);
 
 bool GetGFXBufferFormat(GLenum internal_format, gfx::BufferFormat* out_format);
-bool GetGFXBufferUsage(GLenum buffer_usage, gfx::BufferUsage* out_usage);
 
 bool IsASTCFormat(GLenum internal_format);
 bool IsCompressedTextureFormat(GLenum internal_format);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index a36b9901..c2012717 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3624,11 +3624,11 @@
     }
   }
 
-  // Support for CHROMIUM_texture_storage_image depends on the underlying
+  // Support for texture_storage_image depends on the underlying
   // ImageFactory's ability to create anonymous images.
   gpu::ImageFactory* image_factory = group_->image_factory();
   if (image_factory && image_factory->SupportsCreateAnonymousImage())
-    feature_info_->EnableCHROMIUMTextureStorageImage();
+    feature_info_->EnableTextureStorageImage();
 
   // In theory |needs_emulation| needs to be true on Desktop GL 4.1 or lower.
   // However, we set it to true everywhere, not to trust drivers to handle
@@ -4137,6 +4137,11 @@
   DoGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &caps.max_renderbuffer_size, 1);
   DoGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &caps.max_texture_image_units, 1);
   DoGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.max_texture_size, 1);
+  if (workarounds().webgl_or_caps_max_texture_size) {
+    caps.max_texture_size =
+        std::min(caps.max_texture_size,
+                 feature_info_->workarounds().webgl_or_caps_max_texture_size);
+  }
   DoGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.max_varying_vectors, 1);
   DoGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.max_vertex_attribs, 1);
   DoGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
@@ -4316,7 +4321,7 @@
   }
   caps.texture_npot = feature_info_->feature_flags().npot_ok;
   caps.texture_storage_image =
-      feature_info_->feature_flags().chromium_texture_storage_image;
+      feature_info_->feature_flags().texture_storage_image;
   caps.supports_oop_raster = false;
   caps.chromium_gpu_fence = feature_info_->feature_flags().chromium_gpu_fence;
   caps.separate_stencil_ref_mask_writemask =
@@ -18428,85 +18433,6 @@
                  ContextState::k3D, "glTexStorage3D");
 }
 
-void GLES2DecoderImpl::DoTexStorage2DImageCHROMIUM(GLenum target,
-                                                   GLenum internal_format,
-                                                   GLenum buffer_usage,
-                                                   GLsizei width,
-                                                   GLsizei height) {
-  TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage2DImageCHROMIUM", "width",
-               width, "height", height);
-
-  ScopedGLErrorSuppressor suppressor(
-      "GLES2CmdDecoder::DoTexStorage2DImageCHROMIUM", error_state_.get());
-
-  if (!texture_manager()->ValidForTarget(target, 0, width, height, 1)) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM",
-                       "dimensions out of range");
-    return;
-  }
-
-  TextureRef* texture_ref =
-      texture_manager()->GetTextureInfoForTarget(&state_, target);
-  if (!texture_ref) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM",
-                       "unknown texture for target");
-    return;
-  }
-
-  Texture* texture = texture_ref->texture();
-  if (texture->IsImmutable()) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM",
-                       "texture is immutable");
-    return;
-  }
-
-  gfx::BufferFormat buffer_format;
-  if (!GetGFXBufferFormat(internal_format, &buffer_format)) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, "glTexStorage2DImageCHROMIUM",
-                       "Invalid buffer format");
-    return;
-  }
-
-  gfx::BufferUsage gfx_buffer_usage;
-  if (!GetGFXBufferUsage(buffer_usage, &gfx_buffer_usage)) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, "glTexStorage2DImageCHROMIUM",
-                       "Invalid buffer usage");
-    return;
-  }
-
-  if (!GetContextGroup()->image_factory()) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM",
-                       "Cannot create GL image");
-    return;
-  }
-
-  bool is_cleared = false;
-  scoped_refptr<gl::GLImage> image =
-      GetContextGroup()->image_factory()->CreateAnonymousImage(
-          gfx::Size(width, height), buffer_format, gfx_buffer_usage,
-          gpu::kNullSurfaceHandle, &is_cleared);
-  if (!image || !image->BindTexImage(target)) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM",
-                       "Failed to create or bind GL Image");
-    return;
-  }
-
-  gfx::Rect cleared_rect;
-  if (is_cleared)
-    cleared_rect = gfx::Rect(width, height);
-
-  texture_manager()->SetLevelInfo(
-      texture_ref, target, 0, image->GetInternalFormat(), width, height, 1, 0,
-      image->GetDataFormat(), image->GetDataType(), cleared_rect);
-  texture_manager()->SetLevelImage(texture_ref, target, 0, image.get(),
-                                   Texture::BOUND);
-
-  if (texture->IsAttachedToFramebuffer())
-    framebuffer_state_.clear_state_dirty = true;
-
-  texture->SetImmutable(true, false);
-}
-
 void GLES2DecoderImpl::DoProduceTextureDirectCHROMIUM(
     GLuint client_id,
     const volatile GLbyte* data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 467f97d..0c24173f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -5172,46 +5172,6 @@
   return error::kNoError;
 }
 
-error::Error GLES2DecoderImpl::HandleTexStorage2DImageCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile gles2::cmds::TexStorage2DImageCHROMIUM& c =
-      *static_cast<const volatile gles2::cmds::TexStorage2DImageCHROMIUM*>(
-          cmd_data);
-  if (!features().chromium_texture_storage_image) {
-    return error::kUnknownCommand;
-  }
-
-  GLenum target = static_cast<GLenum>(c.target);
-  GLenum internalFormat = static_cast<GLenum>(c.internalFormat);
-  GLenum bufferUsage = static_cast<GLenum>(c.bufferUsage);
-  GLsizei width = static_cast<GLsizei>(c.width);
-  GLsizei height = static_cast<GLsizei>(c.height);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2DImageCHROMIUM", target,
-                                    "target");
-    return error::kNoError;
-  }
-  if (!validators_->texture_internal_format_storage.IsValid(internalFormat)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2DImageCHROMIUM",
-                                    internalFormat, "internalFormat");
-    return error::kNoError;
-  }
-  if (width < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM",
-                       "width < 0");
-    return error::kNoError;
-  }
-  if (height < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM",
-                       "height < 0");
-    return error::kNoError;
-  }
-  DoTexStorage2DImageCHROMIUM(target, internalFormat, bufferUsage, width,
-                              height);
-  return error::kNoError;
-}
-
 error::Error GLES2DecoderImpl::HandleWindowRectanglesEXTImmediate(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 1c3e3a6..c63c8ed 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1049,11 +1049,11 @@
   InitializeFeatureInfo(attrib_helper.context_type, DisallowedFeatures(),
                         false);
 
-  // Support for CHROMIUM_texture_storage_image depends on the underlying
+  // Support for texture_storage_image depends on the underlying
   // ImageFactory's ability to create anonymous images.
   gpu::ImageFactory* image_factory = group_->image_factory();
   if (image_factory && image_factory->SupportsCreateAnonymousImage()) {
-    feature_info_->EnableCHROMIUMTextureStorageImage();
+    feature_info_->EnableTextureStorageImage();
   }
 
   // Check for required extensions
@@ -1662,10 +1662,10 @@
   caps.image_ab30 = feature_info_->feature_flags().chromium_image_ab30;
   caps.image_ycbcr_p010 =
       feature_info_->feature_flags().chromium_image_ycbcr_p010;
-  if (feature_info_->workarounds().client_max_texture_size) {
+  if (feature_info_->workarounds().webgl_or_caps_max_texture_size) {
     caps.max_texture_size =
         std::min(caps.max_texture_size,
-                 feature_info_->workarounds().client_max_texture_size);
+                 feature_info_->workarounds().webgl_or_caps_max_texture_size);
   }
   caps.max_copy_texture_chromium_size =
       feature_info_->workarounds().max_copy_texture_chromium_size;
@@ -1703,7 +1703,7 @@
 #endif  // BUILDFLAG(IS_WIN)
   caps.texture_npot = feature_info_->feature_flags().npot_ok;
   caps.texture_storage_image =
-      feature_info_->feature_flags().chromium_texture_storage_image;
+      feature_info_->feature_flags().texture_storage_image;
   caps.chromium_gpu_fence = feature_info_->feature_flags().chromium_gpu_fence;
   caps.chromium_nonblocking_readback = true;
   caps.num_surface_buffers = surface_->GetBufferCount();
@@ -2154,7 +2154,7 @@
 
   gpu::ImageFactory* image_factory = group_->image_factory();
   if (image_factory && image_factory->SupportsCreateAnonymousImage()) {
-    feature_info_->EnableCHROMIUMTextureStorageImage();
+    feature_info_->EnableTextureStorageImage();
   }
 }
 
@@ -2259,16 +2259,6 @@
       std::copy(std::begin(scissor_), std::end(scissor_), params);
       break;
 
-    case GL_MAX_TEXTURE_SIZE:
-    case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
-    case GL_MAX_3D_TEXTURE_SIZE:
-      if (feature_info_->workarounds().client_max_texture_size) {
-        *params = std::min(
-            *params, static_cast<T>(
-                         feature_info_->workarounds().client_max_texture_size));
-      }
-      break;
-
     default:
       break;
   }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
index 0f1f6f3..05ba8f39 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -546,11 +546,6 @@
                           GLenum type,
                           GLsizei image_size,
                           const void* pixels);
-error::Error DoTexStorage2DImageCHROMIUM(GLenum target,
-                                         GLenum internalformat,
-                                         GLenum bufferusage,
-                                         GLsizei width,
-                                         GLsizei height);
 error::Error DoTexImage3D(GLenum target,
                           GLint level,
                           GLint internalformat,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 9cf1735..38cd207 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -1404,14 +1404,6 @@
                 "Cannot change the attachments of the default framebuffer.");
     return error::kNoError;
   }
-  if (feature_info_->workarounds().client_max_texture_size && texture) {
-    GLint max_level = base::bits::Log2Floor(
-        feature_info_->workarounds().client_max_texture_size);
-    if (level > max_level) {
-      InsertError(GL_INVALID_VALUE, "Level too large");
-      return error::kNoError;
-    }
-  }
   BindPendingImageForClientIDIfNeeded(texture);
   api()->glFramebufferTexture2DEXTFn(
       target, attachment, textarget,
@@ -3452,61 +3444,6 @@
   return error::kNoError;
 }
 
-error::Error GLES2DecoderPassthroughImpl::DoTexStorage2DImageCHROMIUM(
-    GLenum target,
-    GLenum internalFormat,
-    GLenum bufferUsage,
-    GLsizei width,
-    GLsizei height) {
-  TextureTarget target_enum = GLenumToTextureTarget(target);
-  if (target_enum == TextureTarget::kCubeMap ||
-      target_enum == TextureTarget::kUnkown) {
-    InsertError(GL_INVALID_ENUM, "Invalid target");
-    return error::kNoError;
-  }
-
-  const BoundTexture& bound_texture =
-      bound_textures_[static_cast<size_t>(target_enum)][active_texture_unit_];
-  if (bound_texture.texture == nullptr) {
-    InsertError(GL_INVALID_OPERATION, "No texture bound");
-    return error::kNoError;
-  }
-
-  gfx::BufferFormat buffer_format;
-  if (!GetGFXBufferFormat(internalFormat, &buffer_format)) {
-    InsertError(GL_INVALID_ENUM, "Invalid buffer format");
-    return error::kNoError;
-  }
-
-  gfx::BufferUsage buffer_usage;
-  if (!GetGFXBufferUsage(bufferUsage, &buffer_usage)) {
-    InsertError(GL_INVALID_ENUM, "Invalid buffer usage");
-    return error::kNoError;
-  }
-
-  if (!GetContextGroup()->image_factory()) {
-    InsertError(GL_INVALID_OPERATION, "Cannot create GL image");
-    return error::kNoError;
-  }
-
-  bool is_cleared;
-  scoped_refptr<gl::GLImage> image =
-      GetContextGroup()->image_factory()->CreateAnonymousImage(
-          gfx::Size(width, height), buffer_format, buffer_usage,
-          gpu::kNullSurfaceHandle, &is_cleared);
-  if (!image || !image->BindTexImage(target)) {
-    InsertError(GL_INVALID_OPERATION, "Failed to create or bind GL Image");
-    return error::kNoError;
-  }
-
-  bound_texture.texture->SetLevelImage(target, 0, image.get());
-
-  // Target is already validated
-  UpdateTextureSizeFromTarget(target);
-
-  return error::kNoError;
-}
-
 error::Error GLES2DecoderPassthroughImpl::DoGenQueriesEXT(
     GLsizei n,
     volatile GLuint* queries) {
@@ -4095,11 +4032,11 @@
   InitializeFeatureInfo(feature_info_->context_type(),
                         feature_info_->disallowed_features(), true);
 
-  // Support for CHROMIUM_texture_storage_image depends on the underlying
+  // Support for texture_storage_image depends on the underlying
   // ImageFactory's ability to create anonymous images.
   gpu::ImageFactory* image_factory = group_->image_factory();
   if (image_factory && image_factory->SupportsCreateAnonymousImage()) {
-    feature_info_->EnableCHROMIUMTextureStorageImage();
+    feature_info_->EnableTextureStorageImage();
   }
 
   return error::kNoError;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
index 0b4008bc..41be4b8 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
@@ -4441,29 +4441,6 @@
   return error::kNoError;
 }
 
-error::Error GLES2DecoderPassthroughImpl::HandleTexStorage2DImageCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile gles2::cmds::TexStorage2DImageCHROMIUM& c =
-      *static_cast<const volatile gles2::cmds::TexStorage2DImageCHROMIUM*>(
-          cmd_data);
-  if (!features().chromium_texture_storage_image) {
-    return error::kUnknownCommand;
-  }
-
-  GLenum target = static_cast<GLenum>(c.target);
-  GLenum internalFormat = static_cast<GLenum>(c.internalFormat);
-  GLenum bufferUsage = static_cast<GLenum>(c.bufferUsage);
-  GLsizei width = static_cast<GLsizei>(c.width);
-  GLsizei height = static_cast<GLsizei>(c.height);
-  error::Error error = DoTexStorage2DImageCHROMIUM(target, internalFormat,
-                                                   bufferUsage, width, height);
-  if (error != error::kNoError) {
-    return error;
-  }
-  return error::kNoError;
-}
-
 error::Error GLES2DecoderPassthroughImpl::HandleWindowRectanglesEXTImmediate(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 3770e92..26548ab 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -1299,13 +1299,13 @@
   } else {
     NOTIMPLEMENTED();
   }
-  if (feature_info()->workarounds().client_max_texture_size) {
+  if (feature_info()->workarounds().webgl_or_caps_max_texture_size) {
     caps.max_texture_size =
         std::min(caps.max_texture_size,
-                 feature_info()->workarounds().client_max_texture_size);
+                 feature_info()->workarounds().webgl_or_caps_max_texture_size);
     caps.max_cube_map_texture_size =
         std::min(caps.max_cube_map_texture_size,
-                 feature_info()->workarounds().client_max_texture_size);
+                 feature_info()->workarounds().webgl_or_caps_max_texture_size);
   }
   if (feature_info()->workarounds().max_3d_array_texture_size) {
     caps.max_3d_texture_size =
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 1e2ecc83..db24142 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -229,7 +229,7 @@
     shared_memory_address_ =
         static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_;
 
-    workarounds.client_max_texture_size = INT_MAX - 1;
+    workarounds.webgl_or_caps_max_texture_size = INT_MAX - 1;
     shared_image_factory_ = std::make_unique<SharedImageFactory>(
         GpuPreferences(), workarounds, GpuFeatureInfo(), context_state_.get(),
         &mailbox_manager_, &shared_image_manager_, nullptr, nullptr,
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index 9ec3103..f8533e7 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -262,15 +262,10 @@
 
 bool SharedContextState::InitializeGrContext(
     const GpuPreferences& gpu_preferences,
-    const GpuDriverBugWorkarounds& workarounds_ref,
+    const GpuDriverBugWorkarounds& workarounds,
     gpu::raster::GrShaderCache* cache,
     GpuProcessActivityFlags* activity_flags,
     gl::ProgressReporter* progress_reporter) {
-  // TODO(crbug.com/1319451): Remove workaround for skia. This workaround will
-  // only apply to command buffer clients.
-  GpuDriverBugWorkarounds workarounds(workarounds_ref);
-  workarounds.max_texture_size_limit_4096 = false;
-
   progress_reporter_ = progress_reporter;
   gr_shader_cache_ = cache;
 
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index d89fc826..4db9ecb 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -44,10 +44,9 @@
 
 #if defined(USE_OZONE)
 #include "ui/ozone/buildflags.h"
+#include "ui/ozone/public/gl_ozone.h"
 #include "ui/ozone/public/ozone_platform.h"
-#if BUILDFLAG(OZONE_PLATFORM_X11)
-#include "ui/gl/gl_image_glx_native_pixmap.h"
-#endif
+#include "ui/ozone/public/surface_factory_ozone.h"
 #endif
 
 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_WIN)) && \
@@ -268,7 +267,11 @@
     bool glx_ext_supported = false;
 #if defined(USE_OZONE)
 #if BUILDFLAG(OZONE_PLATFORM_X11)
-    glx_ext_supported = gl::GLImageGLXNativePixmap::CanImportNativePixmap();
+    ui::GLOzone* gl_ozone = ui::OzonePlatform::GetInstance()
+                                ->GetSurfaceFactoryOzone()
+                                ->GetCurrentGLOzone();
+    // This checks for extension support on both GLOzoneEGLX11 and GLOzoneGLX.
+    glx_ext_supported = gl_ozone && gl_ozone->CanImportNativePixmap();
 #endif  // BUILDFLAG(OZONE_PLATFORM_X11)
 #endif  // defined(USE_OZONE)
     if (egl_ext_supported) {
diff --git a/gpu/command_buffer/tests/gl_texture_storage_unittest.cc b/gpu/command_buffer/tests/gl_texture_storage_unittest.cc
index 6013b7a..987a475b 100644
--- a/gpu/command_buffer/tests/gl_texture_storage_unittest.cc
+++ b/gpu/command_buffer/tests/gl_texture_storage_unittest.cc
@@ -94,9 +94,6 @@
     const GLubyte* extensions = glGetString(GL_EXTENSIONS);
     ext_texture_storage_available_ = strstr(
         reinterpret_cast<const char*>(extensions), "GL_EXT_texture_storage");
-    chromium_texture_storage_image_available_ =
-        strstr(reinterpret_cast<const char*>(extensions),
-               "GL_CHROMIUM_texture_storage_image");
   }
 
   void TearDown() override { gl_.Destroy(); }
@@ -106,7 +103,6 @@
   GLuint tex_ = 0;
   GLuint fbo_ = 0;
   bool ext_texture_storage_available_ = false;
-  bool chromium_texture_storage_image_available_ = false;
 };
 
 TEST_F(TextureStorageTest, CorrectPixels) {
@@ -224,22 +220,6 @@
   EXPECT_NE(static_cast<GLenum>(GL_NO_ERROR), glGetError());
 }
 
-TEST_F(TextureStorageTest, CorrectImagePixels) {
-  if (!chromium_texture_storage_image_available_)
-    return;
-
-  glTexStorage2DImageCHROMIUM(GL_TEXTURE_2D, GL_RGBA8_OES, GL_SCANOUT_CHROMIUM,
-                              2, 2);
-
-  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                         tex_, 0);
-
-  uint8_t source_pixels[16] = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
-  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE,
-                  source_pixels);
-  EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 2, 2, 0, source_pixels, nullptr));
-}
-
 TEST_F(TextureStorageTest, LuminanceEmulation) {
   if (!ext_texture_storage_available_)
     return;
diff --git a/gpu/config/gpu_driver_bug_workarounds.cc b/gpu/config/gpu_driver_bug_workarounds.cc
index 37274f1..55c5d167 100644
--- a/gpu/config/gpu_driver_bug_workarounds.cc
+++ b/gpu/config/gpu_driver_bug_workarounds.cc
@@ -28,7 +28,7 @@
   }
   // TODO(crbug.com/1319451): Rename workaround.
   if (workarounds->max_texture_size_limit_4096)
-    workarounds->client_max_texture_size = 4096;
+    workarounds->webgl_or_caps_max_texture_size = 4096;
 
   if (workarounds->max_copy_texture_chromium_size_1048576)
     workarounds->max_copy_texture_chromium_size = 1048576;
@@ -76,8 +76,8 @@
   GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP)
 #undef GPU_OP
 
-  client_max_texture_size =
-      LowerMax(client_max_texture_size, extra.client_max_texture_size);
+  webgl_or_caps_max_texture_size = LowerMax(
+      webgl_or_caps_max_texture_size, extra.webgl_or_caps_max_texture_size);
   max_copy_texture_chromium_size = LowerMax(
       max_copy_texture_chromium_size, extra.max_copy_texture_chromium_size);
   max_3d_array_texture_size =
diff --git a/gpu/config/gpu_driver_bug_workarounds.h b/gpu/config/gpu_driver_bug_workarounds.h
index 7c8350b6..9f7c9ab 100644
--- a/gpu/config/gpu_driver_bug_workarounds.h
+++ b/gpu/config/gpu_driver_bug_workarounds.h
@@ -37,7 +37,7 @@
 #undef GPU_OP
 
   // Note: 0 here means use driver limit.
-  GLint client_max_texture_size = 0;
+  GLint webgl_or_caps_max_texture_size = 0;
   GLint max_3d_array_texture_size = 0;
   GLint max_copy_texture_chromium_size = 0;
 };
diff --git a/infra/OWNERS b/infra/OWNERS
index 20f8e9b..a603432 100644
--- a/infra/OWNERS
+++ b/infra/OWNERS
@@ -11,7 +11,6 @@
 guterman@google.com
 hypan@google.com
 kimstephanie@google.com
-martiniss@chromium.org
 morawand@google.com
 sshrimp@google.com
 svenzheng@chromium.org
diff --git a/infra/config/generated/builders/reclient/Win x64 Builder reclient gVisor cross test/properties.json b/infra/config/generated/builders/reclient/Win x64 Builder reclient gVisor cross test/properties.json
new file mode 100644
index 0000000..46c7ea80
--- /dev/null
+++ b/infra/config/generated/builders/reclient/Win x64 Builder reclient gVisor cross test/properties.json
@@ -0,0 +1,59 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "reclient",
+              "builder": "Win x64 Builder reclient gVisor cross test",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-fyi-archive",
+              "builder_group": "chromium.reclient.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "use_clang_coverage",
+                  "enable_reclient",
+                  "reclient_test"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "reclient",
+          "builder": "Win x64 Builder reclient gVisor cross test",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted-test",
+    "metrics_project": "chromium-reclient-metrics"
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.reclient.fyi",
+  "recipe": "chromium"
+}
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 1c0efe9..7754758 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -46596,6 +46596,86 @@
       }
     }
     builders {
+      name: "Win x64 Builder reclient gVisor cross test"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:32"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Windows"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/reclient/Win x64 Builder reclient gVisor cross test/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.reclient.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 18000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Win x64 Builder reclient staging"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 5d4d48e5..6aa10bb 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -12025,6 +12025,11 @@
     short_name: "rcs"
   }
   builders {
+    name: "buildbucket/luci.chromium.reclient/Win x64 Builder reclient gVisor cross test"
+    category: "rbe|win"
+    short_name: "rcs"
+  }
+  builders {
     name: "buildbucket/luci.chromium.reclient/Win x64 Builder reclient staging"
     category: "rbe|win"
     short_name: "rcs"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index fdbd509..21a73c81 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -3675,6 +3675,16 @@
   }
 }
 job {
+  id: "Win x64 Builder reclient gVisor cross test"
+  realm: "reclient"
+  acl_sets: "reclient"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "reclient"
+    builder: "Win x64 Builder reclient gVisor cross test"
+  }
+}
+job {
   id: "Win x64 Builder reclient staging"
   realm: "reclient"
   acl_sets: "reclient"
@@ -7347,6 +7357,7 @@
   triggers: "Mac Builder reclient test"
   triggers: "Simple Chrome Builder reclient staging"
   triggers: "Simple Chrome Builder reclient test"
+  triggers: "Win x64 Builder reclient gVisor cross test"
   triggers: "Win x64 Builder reclient staging"
   triggers: "Win x64 Builder reclient test"
   triggers: "ios-simulator reclient staging"
diff --git a/infra/config/subprojects/reclient/reclient.star b/infra/config/subprojects/reclient/reclient.star
index 59af1fda..5732c36 100644
--- a/infra/config/subprojects/reclient/reclient.star
+++ b/infra/config/subprojects/reclient/reclient.star
@@ -79,6 +79,18 @@
         **kwargs
     )
 
+def fyi_reclient_gvisor_test_builder(
+        *,
+        name,
+        console_view_category,
+        **kwargs):
+    return fyi_reclient_staging_builder(
+        name = name,
+        console_view_category = console_view_category,
+        reclient_instance = "rbe-chromium-untrusted-test",
+        **kwargs
+    )
+
 fyi_reclient_staging_builder(
     name = "Linux Builder reclient staging",
     builder_spec = builder_config.copy_from(
@@ -207,6 +219,29 @@
     os = os.WINDOWS_ANY,
 )
 
+fyi_reclient_gvisor_test_builder(
+    name = "Win x64 Builder reclient gVisor cross test",
+    builder_spec = builder_config.copy_from(
+        "ci/Win x64 Builder",
+        lambda spec: structs.evolve(
+            spec,
+            gclient_config = structs.extend(
+                spec.gclient_config,
+                apply_configs = [
+                    "enable_reclient",
+                    "reclient_test",
+                ],
+            ),
+            build_gs_bucket = "chromium-fyi-archive",
+        ),
+    ),
+    builderless = True,
+    console_view_category = "win",
+    cores = 32,
+    execution_timeout = 5 * time.hour,
+    os = os.WINDOWS_ANY,
+)
+
 fyi_reclient_staging_builder(
     name = "Simple Chrome Builder reclient staging",
     console_view_category = "linux",
diff --git a/infra/orchestrator/OWNERS b/infra/orchestrator/OWNERS
index 3c6fdec..b492f55 100644
--- a/infra/orchestrator/OWNERS
+++ b/infra/orchestrator/OWNERS
@@ -3,4 +3,3 @@
 gatong@chromium.org
 gbeaty@chromium.org
 kimstephanie@google.com
-martiniss@chromium.org
diff --git a/ios/build/tools/convert_gn_xcodeproj.py b/ios/build/tools/convert_gn_xcodeproj.py
index e0e490c9..82d1c6c 100755
--- a/ios/build/tools/convert_gn_xcodeproj.py
+++ b/ios/build/tools/convert_gn_xcodeproj.py
@@ -200,10 +200,6 @@
     # iterating this list and python dictionaries cannot be mutated
     # during iteration.
 
-    # TODO(crbug.com/1334028) Disable code signing for Xcode 14.
-    lines = check_output(['xcodebuild', '-version']).splitlines()
-    xcode_version_int = int(lines[0].split()[-1].split('.')[0])
-
     for key, obj in list(self.IterObjectsByIsa('XCConfigurationList')):
       # Use the first build configuration as template for creating all the
       # new build configurations.
@@ -211,12 +207,6 @@
       build_config_template['buildSettings']['CONFIGURATION_BUILD_DIR'] = \
           '$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
 
-      # TODO(crbug.com/1334028) Disable code signing for Xcode 14.
-      if xcode_version_int >= 14:
-        build_config_template['buildSettings']['CODE_SIGN_IDENTITY'] = ''
-        build_config_template['buildSettings']['CODE_SIGNING_REQUIRED'] = 'NO'
-        build_config_template['buildSettings']['CODE_SIGNING_ALLOWED'] = 'NO'
-
       # Remove the existing build configurations from the project before
       # creating the new ones.
       for build_config_id in obj['buildConfigurations']:
@@ -357,7 +347,7 @@
   SortFileReferencesByName(project, sources, root_object.get('productRefGroup'))
 
   objects = collections.OrderedDict(sorted(project.objects.items()))
-  WriteXcodeProject(project_dir, json.dumps(json_data))
+  # WriteXcodeProject(project_dir, json.dumps(json_data))
 
 
 def CreateGroup(project, parent_group, group_name, use_relative_paths):
diff --git a/ios/build/tools/setup-gn.py b/ios/build/tools/setup-gn.py
index edab517..9bff449 100755
--- a/ios/build/tools/setup-gn.py
+++ b/ios/build/tools/setup-gn.py
@@ -20,9 +20,6 @@
 SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator', 'maccatalyst')
 SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official')
 
-# Name of the gn variable to set when generating Xcode project.
-GENERATE_XCODE_PROJECT = 'ios_set_attributes_for_xcode_project_generation'
-
 # Pattern matching lines from ~/.lldbinit that must not be copied to the
 # generated .lldbinit file. They match what the user were told to add to
 # their global ~/.lldbinit file before setup-gn.py was updated to generate
@@ -102,7 +99,7 @@
     self._config = config
     self._target = target
 
-  def _GetGnArgs(self, extra_args=None):
+  def _GetGnArgs(self):
     """Build the list of arguments to pass to gn.
 
     Returns:
@@ -140,11 +137,6 @@
         'target_environment',
         self.TARGET_ENVIRONMENT_VALUES[self._target]))
 
-    # If extra arguments are passed to the function, pass them before the
-    # user overrides (if any).
-    if extra_args is not None:
-      args.extend(extra_args)
-
     # Add user overrides after the other configurations so that they can
     # refer to them and override them.
     args.extend(self._settings.items('gn_args'))
@@ -175,8 +167,7 @@
             stream.write('import("%s")\n' % import_rule)
           stream.write('\n')
 
-      extra_args = [(GENERATE_XCODE_PROJECT, xcode_project_name is not None)]
-      gn_args = self._GetGnArgs(extra_args)
+      gn_args = self._GetGnArgs()
 
       for name, value in gn_args:
         if isinstance(value, bool):
@@ -225,6 +216,11 @@
       gn_command.append('--ninja-executable=autoninja')
       gn_command.append('--xcode-build-system=new')
       gn_command.append('--xcode-project=%s' % xcode_project_name)
+      gn_command.append('--xcode-additional-files-patterns=*.md')
+      gn_command.append('--xcode-additional-files-roots=//ios;//ios_internal')
+      gn_command.append('--xcode-configs=' + ';'.join(SUPPORTED_CONFIGS))
+      gn_command.append('--xcode-config-build-dir='
+                        '//out/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}')
       if self._settings.has_section('filters'):
         target_filters = self._settings.values('filters')
         if target_filters:
diff --git a/ios/chrome/app/strings/resources/ios_strings_da.xtb b/ios/chrome/app/strings/resources/ios_strings_da.xtb
index 87b571d..0ab6f20 100644
--- a/ios/chrome/app/strings/resources/ios_strings_da.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_da.xtb
@@ -326,6 +326,7 @@
 <translation id="369349502275246497">Få tilbudt at gemme adgangskoder</translation>
 <translation id="3709582977625132201">Markér som ulæst</translation>
 <translation id="371230970611282515">Forudser og advarer dig om skadelige hændelser, før de opstår.</translation>
+<translation id="37207012422556617">Populære søgninger</translation>
 <translation id="3740397331642243698">Åbner de angivne webadresser i Google Chrome i inkognito.</translation>
 <translation id="3762232513783804601">Udviklet til din iPad</translation>
 <translation id="3771033907050503522">Inkognitofaner</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_mn.xtb b/ios/chrome/app/strings/resources/ios_strings_mn.xtb
index 11a152c..7199d85 100644
--- a/ios/chrome/app/strings/resources/ios_strings_mn.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_mn.xtb
@@ -326,6 +326,7 @@
 <translation id="369349502275246497">Нууц үг хадгалахыг санал болгох</translation>
 <translation id="3709582977625132201">Уншаагүй болгож тэмдэглэх</translation>
 <translation id="371230970611282515">Аюултай үйл явдлын тухай урьдчилан таамаглаж, тохиолдохоос нь өмнө танд анхааруулга өгдөг.</translation>
+<translation id="37207012422556617">Тренд болж буй хайлт</translation>
 <translation id="3740397331642243698">Оруулсан URL-г Google Chrome дээр нууцлалтайгаар нээдэг.</translation>
 <translation id="3762232513783804601">Таны iPad-д зориулан бүтээсэн</translation>
 <translation id="3771033907050503522">Мэдээний нууцлалтай цонх</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_uz.xtb b/ios/chrome/app/strings/resources/ios_strings_uz.xtb
index 66deb80..2ed8b8ac 100644
--- a/ios/chrome/app/strings/resources/ios_strings_uz.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_uz.xtb
@@ -326,6 +326,7 @@
 <translation id="369349502275246497">Parollarni saqlashni taklif qilish</translation>
 <translation id="3709582977625132201">Oʻqilmagan deb belgilash</translation>
 <translation id="371230970611282515">Xavfli tahdidlarni aniqlaydi va sizni ulardan himoya qiladi.</translation>
+<translation id="37207012422556617">Qidiruv trendlari</translation>
 <translation id="3740397331642243698">Kiritilgan URL manzillar Google Chrome brauzerida Inkognito rejimda ochiladi.</translation>
 <translation id="3762232513783804601">iPad uchun ishlab chiqilgan</translation>
 <translation id="3771033907050503522">Inkognito varaqlar</translation>
diff --git a/ios/chrome/browser/mailto_handler/mailto_handler_configuration.h b/ios/chrome/browser/mailto_handler/mailto_handler_configuration.h
index 8c8cb5d..b69c103b 100644
--- a/ios/chrome/browser/mailto_handler/mailto_handler_configuration.h
+++ b/ios/chrome/browser/mailto_handler/mailto_handler_configuration.h
@@ -32,7 +32,7 @@
 // PrefService used by MailtoHandlerService.
 @property(nonatomic, assign) PrefService* localState;
 
-// SingleSignOnService used by DiscoverFeedService.
+// SingleSignOnService used by MailtoHandlerService.
 @property(nonatomic, strong) id<SingleSignOnService> ssoService;
 
 @end
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
index f6751fe..e8ba37b 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
@@ -31,8 +31,8 @@
 #endif
 
 using chrome_test_util::OpenLinkInNewTabButton;
+using chrome_test_util::SettingsDestinationButton;
 using chrome_test_util::SettingsDoneButton;
-using chrome_test_util::SettingsMenuButton;
 using chrome_test_util::SettingsMenuPrivacyButton;
 using chrome_test_util::TabGridCellAtIndex;
 using chrome_test_util::WebViewMatcher;
diff --git a/ios/chrome/browser/policy/policy_egtest.mm b/ios/chrome/browser/policy/policy_egtest.mm
index 88a79c9..1071c65 100644
--- a/ios/chrome/browser/policy/policy_egtest.mm
+++ b/ios/chrome/browser/policy/policy_egtest.mm
@@ -527,8 +527,8 @@
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Set restrictions.
-  std::vector<base::Value> restrictions;
-  restrictions.push_back(base::Value("restricted"));
+  base::Value::List restrictions;
+  restrictions.Append("restricted");
   SetPolicy(base::Value(std::move(restrictions)),
             policy::key::kRestrictAccountsToPatterns);
 
diff --git a/ios/chrome/browser/policy/reporting/profile_report_generator_ios_unittest.mm b/ios/chrome/browser/policy/reporting/profile_report_generator_ios_unittest.mm
index 2adf22e2..9b43385 100644
--- a/ios/chrome/browser/policy/reporting/profile_report_generator_ios_unittest.mm
+++ b/ios/chrome/browser/policy/reporting/profile_report_generator_ios_unittest.mm
@@ -89,7 +89,7 @@
   void InitPolicyMap() {
     policy_map_.Set("kPolicyName1", policy::POLICY_LEVEL_MANDATORY,
                     policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
-                    base::Value(std::vector<base::Value>()), nullptr);
+                    base::Value(base::Value::List()), nullptr);
     policy_map_.Set("kPolicyName2", policy::POLICY_LEVEL_RECOMMENDED,
                     policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_MERGED,
                     base::Value(true), nullptr);
diff --git a/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm b/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm
index d33a467..b0bd8a31 100644
--- a/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm
+++ b/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm
@@ -76,7 +76,7 @@
   void InitPolicyMap() {
     policy_map_.Set("kPolicyName1", policy::POLICY_LEVEL_MANDATORY,
                     policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
-                    base::Value(std::vector<base::Value>()), nullptr);
+                    base::Value(base::Value::List()), nullptr);
     policy_map_.Set("kPolicyName2", policy::POLICY_LEVEL_RECOMMENDED,
                     policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_MERGED,
                     base::Value(true), nullptr);
diff --git a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_egtest.mm b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_egtest.mm
index 3269be9..ee2511fc 100644
--- a/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/advanced_settings_signin/advanced_settings_signin_egtest.mm
@@ -302,7 +302,8 @@
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::BookmarksDestinationButton()];
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsLink()] performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
index bff9a31..56d4e414 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
@@ -327,7 +327,8 @@
 
   // Open Bookmarks and tap on Sign In promo button.
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::BookmarksDestinationButton()];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
 
   // Assert sign-in screen was shown.
@@ -347,7 +348,8 @@
   // Re-open the sign-in screen. If it wasn't correctly dismissed previously,
   // this will fail.
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::BookmarksDestinationButton()];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
   [[EarlGrey selectElementWithMatcher:IdentityCellMatcherForEmail(
                                           fakeIdentity.userEmail)]
@@ -509,7 +511,7 @@
     case OpenSigninMethodFromBookmarks:
       [ChromeEarlGreyUI openToolsMenu];
       [ChromeEarlGreyUI
-          tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
+          tapToolsMenuButton:chrome_test_util::BookmarksDestinationButton()];
       [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
           performAction:grey_tap()];
       break;
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.mm
index f904ac6c..209475d 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.mm
@@ -258,7 +258,7 @@
 + (void)tapPrimarySignInButtonInRecentTabs {
   [ChromeEarlGreyUI openToolsMenu];
   [ChromeEarlGreyUI
-      tapToolsMenuButton:chrome_test_util::RecentTabsMenuButton()];
+      tapToolsMenuButton:chrome_test_util::RecentTabsDestinationButton()];
   [[[EarlGrey
       selectElementWithMatcher:grey_allOf(PrimarySignInButton(),
                                           grey_sufficientlyVisible(), nil)]
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_earl_grey_ui.mm b/ios/chrome/browser/ui/bookmarks/bookmark_earl_grey_ui.mm
index 7b30441..bf04e05 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_earl_grey_ui.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_earl_grey_ui.mm
@@ -34,7 +34,7 @@
 #define EarlGrey [self earlGrey]
 #pragma clang diagnostic pop
 
-using chrome_test_util::BookmarksMenuButton;
+using chrome_test_util::BookmarksDestinationButton;
 using chrome_test_util::BookmarksSaveEditDoneButton;
 using chrome_test_util::BookmarksSaveEditFolderButton;
 using chrome_test_util::ButtonWithAccessibilityLabelId;
@@ -43,12 +43,12 @@
 using chrome_test_util::ContextBarTrailingButtonWithLabel;
 using chrome_test_util::ContextMenuCopyButton;
 using chrome_test_util::CopyLinkButton;
+using chrome_test_util::DeleteButton;
 using chrome_test_util::EditButton;
 using chrome_test_util::MoveButton;
-using chrome_test_util::ShareButton;
-using chrome_test_util::DeleteButton;
-using chrome_test_util::OpenLinkInNewTabButton;
 using chrome_test_util::OpenLinkInIncognitoButton;
+using chrome_test_util::OpenLinkInNewTabButton;
+using chrome_test_util::ShareButton;
 using chrome_test_util::TabGridEditButton;
 using chrome_test_util::TappableBookmarkNodeWithLabel;
 
@@ -120,20 +120,20 @@
 - (void)openBookmarks {
   // Opens the bookmark manager.
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:BookmarksMenuButton()];
+  [ChromeEarlGreyUI tapToolsMenuButton:BookmarksDestinationButton()];
 
   // Assert the menu is gone.
-  [[EarlGrey selectElementWithMatcher:BookmarksMenuButton()]
+  [[EarlGrey selectElementWithMatcher:BookmarksDestinationButton()]
       assertWithMatcher:grey_nil()];
 }
 
 - (void)openBookmarksInWindowWithNumber:(int)windowNumber {
   // Opens the bookmark manager.
   [ChromeEarlGreyUI openToolsMenuInWindowWithNumber:windowNumber];
-  [ChromeEarlGreyUI tapToolsMenuButton:BookmarksMenuButton()];
+  [ChromeEarlGreyUI tapToolsMenuButton:BookmarksDestinationButton()];
 
   // Assert the menu is gone.
-  [[EarlGrey selectElementWithMatcher:BookmarksMenuButton()]
+  [[EarlGrey selectElementWithMatcher:BookmarksDestinationButton()]
       assertWithMatcher:grey_nil()];
 }
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
index 85beaff..2a25678 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
@@ -35,6 +35,7 @@
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
 #import "ios/chrome/browser/ui/commands/page_info_commands.h"
+#import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/commands/text_zoom_commands.h"
 #import "ios/chrome/browser/ui/download/download_manager_coordinator.h"
 #import "ios/chrome/browser/ui/main/scene_state.h"
@@ -148,6 +149,10 @@
         OCMProtocolMock(@protocol(PageInfoCommands));
     [dispatcher startDispatchingToTarget:mockPageInfoCommandHandler
                              forProtocol:@protocol(PageInfoCommands)];
+    id mockQrScannerCommandHandler =
+        OCMProtocolMock(@protocol(QRScannerCommands));
+    [dispatcher startDispatchingToTarget:mockQrScannerCommandHandler
+                             forProtocol:@protocol(QRScannerCommands)];
 
     // Set up ApplicationCommands mock. Because ApplicationCommands conforms
     // to ApplicationSettingsCommands, that needs to be mocked and dispatched
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index b6a6771b..aedfac9 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -23,12 +23,8 @@
 // which in practice is the BrowserViewController instance displaying the tab.
 @protocol BrowserCommands <
     NSObject,
-    // TODO(crbug.com/1323758):Remove PageInfoCommands conformance.
-    PageInfoCommands,
     // TODO(crbug.com/1323764): Remove PopupMenuCommands conformance.
     PopupMenuCommands,
-    // TODO(crbug.com/1323775): Remove QRScannerCommands conformance.
-    QRScannerCommands,
     // TODO(crbug.com/1323778): Remove SnackbarCommands conformance.
     SnackbarCommands>
 
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm
index fa72f9d..af4b407d5 100644
--- a/ios/chrome/browser/ui/history/history_ui_egtest.mm
+++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -48,10 +48,6 @@
 char kResponse2[] = "Test Page 2 content";
 char kResponse3[] = "Test Page 3 content";
 
-// Matcher for the history button in the tools menu.
-id<GREYMatcher> HistoryButton() {
-  return grey_accessibilityID(kToolsMenuHistoryId);
-}
 // Matcher for the edit button in the navigation bar.
 id<GREYMatcher> NavigationEditButton() {
   return grey_accessibilityID(kHistoryToolbarEditButtonIdentifier);
@@ -856,12 +852,14 @@
 
 - (void)openHistoryPanel {
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:HistoryButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::HistoryDestinationButton()];
 }
 
 - (void)openHistoryPanelInWindowWithNumber:(int)windowNumber {
   [ChromeEarlGreyUI openToolsMenuInWindowWithNumber:windowNumber];
-  [ChromeEarlGreyUI tapToolsMenuButton:HistoryButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::HistoryDestinationButton()];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm b/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm
index a623e0e..f395a7f 100644
--- a/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm
+++ b/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm
@@ -21,7 +21,7 @@
 #endif
 
 using chrome_test_util::BookmarksNavigationBarDoneButton;
-using chrome_test_util::RecentTabsMenuButton;
+using chrome_test_util::RecentTabsDestinationButton;
 using chrome_test_util::SettingsDoneButton;
 
 // Test cases to verify that keyboard commands are and are not registered when
@@ -143,7 +143,8 @@
 - (void)testKeyboardCommandsNotRegistered_BookmarksPresented {
   // Open Bookmarks
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::BookmarksDestinationButton()];
   [ChromeEarlGreyUI waitForAppToIdle];
 
   [self verifyNoKeyboardCommandsAreRegistered];
@@ -157,7 +158,7 @@
 - (void)testKeyboardCommands_RecentTabsPresented {
   // Open Recent Tabs
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:RecentTabsMenuButton()];
+  [ChromeEarlGreyUI tapToolsMenuButton:RecentTabsDestinationButton()];
   [ChromeEarlGreyUI waitForAppToIdle];
 
   [self verifyNoKeyboardCommandsAreRegistered];
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
index 779519e..d5a2ea6 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
@@ -19,6 +19,9 @@
 #include "ios/chrome/browser/history/history_service_factory.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/main/scene_state.h"
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h"
@@ -30,6 +33,8 @@
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #import "ios/web/public/test/web_task_environment.h"
 #include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -106,6 +111,26 @@
         0, std::move(web_state), WebStateList::INSERT_FORCE_INDEX,
         WebStateOpener());
 
+    CommandDispatcher* dispatcher = browser_->GetCommandDispatcher();
+
+    id mockQrScannerCommandHandler =
+        OCMProtocolMock(@protocol(QRScannerCommands));
+    [dispatcher startDispatchingToTarget:mockQrScannerCommandHandler
+                             forProtocol:@protocol(QRScannerCommands)];
+
+    // Set up ApplicationCommands mock. Because ApplicationCommands conforms
+    // to ApplicationSettingsCommands, that needs to be mocked and dispatched
+    // as well.
+    id mockApplicationCommandHandler =
+        OCMProtocolMock(@protocol(ApplicationCommands));
+    id mockApplicationSettingsCommandHandler =
+        OCMProtocolMock(@protocol(ApplicationSettingsCommands));
+    [dispatcher startDispatchingToTarget:mockApplicationCommandHandler
+                             forProtocol:@protocol(ApplicationCommands)];
+    [dispatcher
+        startDispatchingToTarget:mockApplicationSettingsCommandHandler
+                     forProtocol:@protocol(ApplicationSettingsCommands)];
+
     delegate_ = [[TestToolbarCoordinatorDelegate alloc] init];
 
     coordinator_ = [[LocationBarCoordinator alloc]
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h
index f91ecf33..8f46767 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h
@@ -11,6 +11,7 @@
 @protocol BrowserCommands;
 @class NamedGuide;
 @class OmniboxTextFieldIOS;
+@protocol QRScannerCommands;
 
 // Delegate protocol for the KeyboardAccessoryView.
 @protocol OmniboxAssistiveKeyboardDelegate
@@ -31,9 +32,9 @@
 @interface OmniboxAssistiveKeyboardDelegateImpl
     : NSObject <OmniboxAssistiveKeyboardDelegate>
 
-// TODO(crbug.com/1323775): This should just be an id<QRScannerCommands> handler
-// (not named 'dispatcher').
-@property(nonatomic, weak) id<ApplicationCommands, BrowserCommands> dispatcher;
+@property(nonatomic, weak) id<ApplicationCommands> applicationCommandsHandler;
+@property(nonatomic, weak) id<BrowserCommands> browserCommandsHandler;
+@property(nonatomic, weak) id<QRScannerCommands> qrScannerCommandsHandler;
 @property(nonatomic, weak) OmniboxTextFieldIOS* omniboxTextField;
 @property(nonatomic, weak) NamedGuide* voiceSearchButtonGuide;
 
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm
index 2175486..98827991 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm
@@ -8,6 +8,7 @@
 #include "base/metrics/user_metrics_action.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_constants.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
@@ -19,7 +20,9 @@
 
 @implementation OmniboxAssistiveKeyboardDelegateImpl
 
-@synthesize dispatcher = _dispatcher;
+@synthesize applicationCommandsHandler = _applicationCommandsHandler;
+@synthesize browserCommandsHandler = _browserCommandsHandler;
+@synthesize qrScannerCommandsHandler = _qrScannerCommandsHandler;
 @synthesize omniboxTextField = _omniboxTextField;
 @synthesize voiceSearchButtonGuide = _voiceSearchButtonGuide;
 
@@ -27,7 +30,7 @@
 
 - (void)keyboardAccessoryVoiceSearchTouchUpInside:(UIView*)view {
   if (ios::provider::IsVoiceSearchEnabled()) {
-    [self.dispatcher preloadVoiceSearch];
+    [self.browserCommandsHandler preloadVoiceSearch];
     base::RecordAction(base::UserMetricsAction("MobileCustomRowVoiceSearch"));
     // Since the keyboard accessory view is in a different window than the main
     // UIViewController upon which Voice Search will be presented, the guide
@@ -44,13 +47,13 @@
           CGRectGetHeight(frame);
       self.voiceSearchButtonGuide.constrainedFrame = frame;
     }
-    [self.dispatcher startVoiceSearch];
+    [self.applicationCommandsHandler startVoiceSearch];
   }
 }
 
 - (void)keyboardAccessoryCameraSearchTouchUp {
   base::RecordAction(base::UserMetricsAction("MobileCustomRowCameraSearch"));
-  [self.dispatcher showQRScanner];
+  [self.qrScannerCommandsHandler showQRScanner];
 }
 
 - (void)keyPressed:(NSString*)title {
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
index c2a1963..3e732cdc 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
@@ -17,9 +17,12 @@
 #include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/load_query_commands.h"
 #import "ios/chrome/browser/ui/commands/omnibox_commands.h"
+#import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/commands/thumb_strip_commands.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h"
 #import "ios/chrome/browser/ui/gestures/view_revealing_animatee.h"
@@ -120,11 +123,14 @@
           self.browser->GetCommandDispatcher());
 
   self.keyboardDelegate = [[OmniboxAssistiveKeyboardDelegateImpl alloc] init];
+  self.keyboardDelegate.applicationCommandsHandler = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), ApplicationCommands);
+  self.keyboardDelegate.qrScannerCommandsHandler = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), QRScannerCommands);
   // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
   // clean up.
-  self.keyboardDelegate.dispatcher =
-      static_cast<id<ApplicationCommands, BrowserCommands>>(
-          self.browser->GetCommandDispatcher());
+  self.keyboardDelegate.browserCommandsHandler =
+      static_cast<id<BrowserCommands>>(self.browser->GetCommandDispatcher());
   self.keyboardDelegate.omniboxTextField = self.textField;
   ConfigureAssistiveKeyboardViews(self.textField, kDotComTLD,
                                   self.keyboardDelegate);
diff --git a/ios/chrome/browser/ui/page_info/page_info_coordinator.mm b/ios/chrome/browser/ui/page_info/page_info_coordinator.mm
index 0a60505..0e1e4453 100644
--- a/ios/chrome/browser/ui/page_info/page_info_coordinator.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_coordinator.mm
@@ -8,8 +8,8 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "ios/chrome/browser/main/browser.h"
-#include "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/commands/page_info_commands.h"
 #import "ios/chrome/browser/ui/page_info/page_info_permissions_mediator.h"
 #import "ios/chrome/browser/ui/page_info/page_info_site_security_description.h"
 #import "ios/chrome/browser/ui/page_info/page_info_site_security_mediator.h"
@@ -58,8 +58,8 @@
       self.viewController;
 
   self.dispatcher = self.browser->GetCommandDispatcher();
-  self.viewController.handler =
-      static_cast<id<BrowserCommands>>(self.browser->GetCommandDispatcher());
+  self.viewController.pageInfoCommandsHandler = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), PageInfoCommands);
 
   if (@available(iOS 15.0, *)) {
     if (web::features::IsMediaPermissionsControlEnabled()) {
diff --git a/ios/chrome/browser/ui/page_info/page_info_view_controller.h b/ios/chrome/browser/ui/page_info/page_info_view_controller.h
index 116ecd5..69a692dd 100644
--- a/ios/chrome/browser/ui/page_info/page_info_view_controller.h
+++ b/ios/chrome/browser/ui/page_info/page_info_view_controller.h
@@ -11,7 +11,7 @@
 #import "ios/chrome/browser/ui/permissions/permissions_consumer.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 
-@protocol BrowserCommands;
+@protocol PageInfoCommands;
 @protocol PermissionsDelegate;
 
 // View Controller for displaying the page info.
@@ -26,9 +26,7 @@
 
 - (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
 
-// Handler used to navigate outside the page info.
-// TODO(crbug.com/1323758): This should just be id<PageInfoCommands>.
-@property(nonatomic, weak) id<BrowserCommands> handler;
+@property(nonatomic, weak) id<PageInfoCommands> pageInfoCommandsHandler;
 
 // Delegate used to handle permission actions.
 @property(nonatomic, weak) id<PermissionsDelegate> permissionsDelegate
diff --git a/ios/chrome/browser/ui/page_info/page_info_view_controller.mm b/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
index 30e7729..e5a587b 100644
--- a/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
@@ -9,7 +9,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/net/crurl.h"
-#include "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/commands/page_info_commands.h"
 #import "ios/chrome/browser/ui/page_info/page_info_constants.h"
 #import "ios/chrome/browser/ui/permissions/permission_info.h"
 #import "ios/chrome/browser/ui/permissions/permissions_constants.h"
@@ -94,7 +94,7 @@
 
   UIBarButtonItem* dismissButton = [[UIBarButtonItem alloc]
       initWithBarButtonSystemItem:UIBarButtonSystemItemDone
-                           target:self.handler
+                           target:self.pageInfoCommandsHandler
                            action:@selector(hidePageInfo)];
   self.navigationItem.rightBarButtonItem = dismissButton;
   self.tableView.separatorInset =
@@ -242,14 +242,14 @@
 
 - (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(CrURL*)URL {
   DCHECK(URL.gurl == GURL(kPageInfoHelpCenterURL));
-  [self.handler showSecurityHelpPage];
+  [self.pageInfoCommandsHandler showSecurityHelpPage];
 }
 
 #pragma mark - UIAdaptivePresentationControllerDelegate
 
 - (void)presentationControllerDidDismiss:
     (UIPresentationController*)presentationController {
-  [self.handler hidePageInfo];
+  [self.pageInfoCommandsHandler hidePageInfo];
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.h b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.h
index 900b42a0..629e441 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.h
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.h
@@ -23,6 +23,7 @@
 @protocol BrowserCoordinatorCommands;
 class BrowserPolicyConnectorIOS;
 class OverlayPresenter;
+@protocol PageInfoCommands;
 class PrefService;
 @protocol FindInPageCommands;
 @protocol TextZoomCommands;
@@ -43,8 +44,6 @@
 @property(nonatomic, assign) WebStateList* webStateList;
 
 // Dispatcher.
-// TODO(crbug.com/1323758): This uses PageInfoCommands via inclusion in
-// BrowserCommands, and should instead use a dedicated handler.
 // TODO(crbug.com/1323764): This uses PopupMenuCommands via inclusion in
 // BrowserCommands, and should instead use a dedicated handler.
 @property(nonatomic, weak) id<ApplicationCommands,
@@ -54,6 +53,8 @@
                               TextZoomCommands>
     dispatcher;
 
+@property(nonatomic, weak) id<PageInfoCommands> pageInfoCommandsHandler;
+
 // Navigation agent for reloading pages.
 @property(nonatomic, assign) WebNavigationBrowserAgent* navigationAgent;
 
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
index 2cd04d5..1b12e11d 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
@@ -43,6 +43,7 @@
 #import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
 #import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
+#import "ios/chrome/browser/ui/commands/page_info_commands.h"
 #import "ios/chrome/browser/ui/commands/reading_list_add_command.h"
 #import "ios/chrome/browser/ui/commands/text_zoom_commands.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
@@ -451,7 +452,7 @@
       [self createOverflowMenuDestination:passwordTitleID
                               destination:overflow_menu::Destination::Passwords
                                 imageName:passwordIconImageName
-                          accessibilityID:@""
+                          accessibilityID:kToolsMenuPasswordsId
                                   handler:^{
                                     [weakSelf openPasswords];
                                   }];
@@ -1507,9 +1508,7 @@
 // Dismisses the menu and shows page information.
 - (void)openSiteInformation {
   [self.dispatcher dismissPopupMenuAnimated:YES];
-  // TODO(crbug.com/1323758): This will need to be called on the
-  // PageInfoCommands handler.
-  [self.dispatcher showPageInfo];
+  [self.pageInfoCommandsHandler showPageInfo];
 }
 
 // Dismisses the menu and opens settings, firing metrics for the settings
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.h b/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.h
index 655a2cd..ebbecd5 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.h
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.h
@@ -14,7 +14,9 @@
 @protocol BrowserCoordinatorCommands;
 @protocol FindInPageCommands;
 @protocol LoadQueryCommands;
+@protocol PageInfoCommands;
 @protocol PopupMenuActionHandlerDelegate;
+@protocol QRScannerCommands;
 @protocol TextZoomCommands;
 class WebNavigationBrowserAgent;
 
@@ -29,12 +31,8 @@
 @property(nonatomic, weak) id<PopupMenuActionHandlerDelegate> delegate;
 
 // Dispatcher.
-// TODO(crbug.com/1323758): This uses PageInfoCommands via inclusion in
-// BrowserCommands, and should instead use a dedicated handler.
 // TODO(crbug.com/1323764): This uses PopupMenuCommands via inclusion in
 // BrowserCommands, and should instead use a dedicated handler.
-// TODO(crbug.com/1323775): This uses  QRScannerCommands via inclusion in
-// BrowserCommands, and should instead use a dedicated handler.
 
 @property(nonatomic, weak) id<ApplicationCommands,
                               BrowserCommands,
@@ -44,6 +42,8 @@
                               TextZoomCommands>
     dispatcher;
 
+@property(nonatomic, weak) id<PageInfoCommands> pageInfoCommandsHandler;
+@property(nonatomic, weak) id<QRScannerCommands> qrScannerCommandsHandler;
 @property(nonatomic, assign) WebNavigationBrowserAgent* navigationAgent;
 
 @end
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm
index 00664e770..e60d819 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_action_handler.mm
@@ -19,6 +19,7 @@
 #import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
 #import "ios/chrome/browser/ui/commands/load_query_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
+#import "ios/chrome/browser/ui/commands/page_info_commands.h"
 #import "ios/chrome/browser/ui/commands/text_zoom_commands.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_action_handler_delegate.h"
@@ -96,9 +97,7 @@
       break;
     case PopupMenuActionSiteInformation:
       RecordAction(UserMetricsAction("MobileMenuSiteInformation"));
-      // TODO(crbug.com/1323758): This will need to be called on the
-      // PageInfoCommands handler.
-      [self.dispatcher showPageInfo];
+      [self.pageInfoCommandsHandler showPageInfo];
       break;
     case PopupMenuActionReportIssue:
       RecordAction(UserMetricsAction("MobileMenuReportAnIssue"));
@@ -197,9 +196,7 @@
     }
     case PopupMenuActionQRCodeSearch:
       RecordAction(UserMetricsAction("MobileMenuScanQRCode"));
-      // TODO(crbug.com/1323775): This will need to be called on the
-      // QRScannerCommands handler.
-      [self.dispatcher showQRScanner];
+      [self.qrScannerCommandsHandler showQRScanner];
       break;
     case PopupMenuActionSearchCopiedImage: {
       RecordAction(UserMetricsAction("MobileMenuSearchCopiedImage"));
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_constants.h b/ios/chrome/browser/ui/popup_menu/popup_menu_constants.h
index 6e7b5c7..b2bf466 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_constants.h
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_constants.h
@@ -42,6 +42,8 @@
 extern NSString* const kToolsMenuOtherDevicesId;
 // History item accessibility Identifier.
 extern NSString* const kToolsMenuHistoryId;
+// Passwords item accessibility Identifier.
+extern NSString* const kToolsMenuPasswordsId;
 // Report an issue item accessibility Identifier.
 extern NSString* const kToolsMenuReportAnIssueId;
 // Translate item accessibility Identifier.
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_constants.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_constants.mm
index b570a1e..374b80d 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_constants.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_constants.mm
@@ -32,6 +32,7 @@
 NSString* const kToolsMenuReadingListId = @"kToolsMenuReadingListId";
 NSString* const kToolsMenuOtherDevicesId = @"kToolsMenuOtherDevicesId";
 NSString* const kToolsMenuHistoryId = @"kToolsMenuHistoryId";
+NSString* const kToolsMenuPasswordsId = @"kToolsMenuPasswordsId";
 NSString* const kToolsMenuReportAnIssueId = @"kToolsMenuReportAnIssueId";
 NSString* const kToolsMenuTranslateId = @"kToolsMenuTranslateId";
 NSString* const kToolsMenuFindInPageId = @"kToolsMenuFindInPageId";
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
index c49a58b..c2f0b03 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
@@ -27,7 +27,9 @@
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
+#import "ios/chrome/browser/ui/commands/page_info_commands.h"
 #import "ios/chrome/browser/ui/commands/popup_menu_commands.h"
+#import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/feature_flags.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_swift.h"
@@ -349,6 +351,8 @@
             id<ApplicationCommands, BrowserCommands, BrowserCoordinatorCommands,
                FindInPageCommands, TextZoomCommands>>(
             self.browser->GetCommandDispatcher());
+        self.overflowMenuMediator.pageInfoCommandsHandler = HandlerForProtocol(
+            self.browser->GetCommandDispatcher(), PageInfoCommands);
         self.overflowMenuMediator.webStateList =
             self.browser->GetWebStateList();
         self.overflowMenuMediator.navigationAgent =
@@ -471,6 +475,10 @@
       id<ApplicationCommands, BrowserCommands, BrowserCoordinatorCommands,
          FindInPageCommands, LoadQueryCommands, TextZoomCommands>>(
       self.browser->GetCommandDispatcher());
+  self.actionHandler.pageInfoCommandsHandler = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), PageInfoCommands);
+  self.actionHandler.qrScannerCommandsHandler = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), QRScannerCommands);
   self.actionHandler.delegate = self.mediator;
   self.actionHandler.navigationAgent =
       WebNavigationBrowserAgent::FromBrowser(self.browser);
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index bc82826f9..2232ff56 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -236,7 +236,7 @@
 void OpenReadingList() {
   [ChromeEarlGreyUI openToolsMenu];
   [ChromeEarlGreyUI
-      tapToolsMenuButton:chrome_test_util::ReadingListMenuButton()];
+      tapToolsMenuButton:chrome_test_util::ReadingListDestinationButton()];
   // It seems that sometimes there is a delay before the ReadingList is
   // displayed. See https://crbug.com/1109202 .
   GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
@@ -453,7 +453,7 @@
   [ChromeEarlGreyUI openToolsMenu];
   // Tap on the Page Info button.
   [ChromeEarlGreyUI
-      tapToolsMenuButton:grey_accessibilityID(kToolsMenuSiteInformation)];
+      tapToolsMenuButton:chrome_test_util::SiteInfoDestinationButton()];
 }
 
 // Tests that the correct version of kDistillableURL is displayed.
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
index 85b883a..a037476 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -36,7 +36,7 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::RecentTabsMenuButton;
+using chrome_test_util::RecentTabsDestinationButton;
 
 namespace {
 const char kURLOfTestPage[] = "http://testPage";
@@ -56,7 +56,7 @@
   }
 
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:RecentTabsMenuButton()];
+  [ChromeEarlGreyUI tapToolsMenuButton:RecentTabsDestinationButton()];
 }
 
 // Returns the matcher for the Recent Tabs table.
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_egtest.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_egtest.mm
index d72547f..015ae6a 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_egtest.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_egtest.mm
@@ -29,11 +29,6 @@
 // URL of the help center page.
 char kHelpCenterURL[] = "support.google.com";
 
-// Matcher for the history button in the tools menu.
-id<GREYMatcher> HistoryButton() {
-  return grey_accessibilityID(kToolsMenuHistoryId);
-}
-
 // Matcher for an element with or without the
 // UIAccessibilityTraitSelected accessibility trait depending on `selected`.
 id<GREYMatcher> ElementIsSelected(BOOL selected) {
@@ -281,7 +276,8 @@
 // the "Learn more" link opens the help center.
 - (void)testTapLearnMoreFromHistory {
   [ChromeEarlGreyUI openToolsMenu];
-  [ChromeEarlGreyUI tapToolsMenuButton:HistoryButton()];
+  [ChromeEarlGreyUI
+      tapToolsMenuButton:chrome_test_util::HistoryDestinationButton()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           HistoryClearBrowsingDataButton()]
       performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index a711f3a2..caa7ae96 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -65,12 +65,12 @@
     "password_issues_presenter.h",
     "password_issues_table_view_controller.h",
     "password_issues_table_view_controller.mm",
+    "password_manager_view_controller.h",
+    "password_manager_view_controller.mm",
+    "password_manager_view_controller_delegate.h",
+    "password_manager_view_controller_presentation_delegate.h",
     "passwords_consumer.h",
     "passwords_settings_commands.h",
-    "passwords_table_view_controller.h",
-    "passwords_table_view_controller.mm",
-    "passwords_table_view_controller_delegate.h",
-    "passwords_table_view_controller_presentation_delegate.h",
   ]
   deps = [
     ":password_constants",
@@ -143,8 +143,8 @@
     "password_exporter_unittest.mm",
     "password_issues_mediator_unittest.mm",
     "password_issues_table_view_controller_unittest.mm",
+    "password_manager_view_controller_unittest.mm",
     "passwords_mediator_unittest.mm",
-    "passwords_table_view_controller_unittest.mm",
   ]
   deps = [
     ":password",
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h b/ios/chrome/browser/ui/settings/password/password_manager_view_controller.h
similarity index 72%
rename from ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
rename to ios/chrome/browser/ui/settings/password/password_manager_view_controller.h
index 1490cb27..6f2c7a5 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h
+++ b/ios/chrome/browser/ui/settings/password/password_manager_view_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_H_
 
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/settings_controller_protocol.h"
@@ -15,10 +15,10 @@
 class Browser;
 @class PasswordExporter;
 @protocol PasswordsSettingsCommands;
-@protocol PasswordsTableViewControllerDelegate;
-@protocol PasswordsTableViewControllerPresentationDelegate;
+@protocol PasswordManagerViewControllerDelegate;
+@protocol PasswordManagerViewControllerPresentationDelegate;
 
-@interface PasswordsTableViewController
+@interface PasswordManagerViewController
     : SettingsRootTableViewController <PasswordsConsumer,
                                        SettingsControllerProtocol>
 
@@ -34,9 +34,9 @@
 @property(nonatomic, weak) id<PasswordsSettingsCommands> handler;
 
 // Delegate.
-@property(nonatomic, weak) id<PasswordsTableViewControllerDelegate> delegate;
+@property(nonatomic, weak) id<PasswordManagerViewControllerDelegate> delegate;
 
-@property(nonatomic, weak) id<PasswordsTableViewControllerPresentationDelegate>
+@property(nonatomic, weak) id<PasswordManagerViewControllerPresentationDelegate>
     presentationDelegate;
 
 // Reauthentication module.
@@ -48,4 +48,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_manager_view_controller.mm
similarity index 98%
rename from ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
rename to ios/chrome/browser/ui/settings/password/password_manager_view_controller.mm
index 393c5f2..30c1c10 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_manager_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller.h"
 
 #import <UIKit/UIKit.h>
 
@@ -39,11 +39,11 @@
 #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
 #import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/password_exporter.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller_presentation_delegate.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_settings_commands.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h"
 #import "ios/chrome/browser/ui/settings/utils/password_auto_fill_status_manager.h"
 #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
 #import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
@@ -208,7 +208,7 @@
 
 @end
 
-@interface PasswordsTableViewController () <
+@interface PasswordManagerViewController () <
     BooleanObserver,
     ChromeAccountManagerServiceObserver,
     PasswordExporterDelegate,
@@ -315,7 +315,7 @@
 
 @end
 
-@implementation PasswordsTableViewController
+@implementation PasswordManagerViewController
 
 #pragma mark - Initialization
 
@@ -477,7 +477,7 @@
 - (void)didMoveToParentViewController:(UIViewController*)parent {
   [super didMoveToParentViewController:parent];
   if (!parent) {
-    [self.presentationDelegate passwordsTableViewControllerDismissed];
+    [self.presentationDelegate PasswordManagerViewControllerDismissed];
   }
 }
 
@@ -716,7 +716,7 @@
 - (BOOL)shouldHideToolbar {
   if (base::FeatureList::IsEnabled(
           password_manager::features::kSupportForAddPasswordsInSettings)) {
-      return NO;
+    return NO;
   }
 
   return [super shouldHideToolbar];
@@ -1100,7 +1100,6 @@
   [self presentViewController:errorInfoPopover animated:YES completion:nil];
 }
 
-
 #pragma mark - PasswordsConsumer
 
 - (void)setPasswordCheckUIState:(PasswordCheckUIState)state
@@ -1698,12 +1697,12 @@
                              }];
   [exportConfirmation addAction:cancelAction];
 
-  __weak PasswordsTableViewController* weakSelf = self;
+  __weak PasswordManagerViewController* weakSelf = self;
   UIAlertAction* exportAction = [UIAlertAction
       actionWithTitle:l10n_util::GetNSString(IDS_IOS_EXPORT_PASSWORDS)
                 style:UIAlertActionStyleDefault
               handler:^(UIAlertAction* action) {
-                PasswordsTableViewController* strongSelf = weakSelf;
+                PasswordManagerViewController* strongSelf = weakSelf;
                 if (!strongSelf) {
                   return;
                 }
@@ -1758,10 +1757,10 @@
   RemoveFormsToBeDeleted(_blockedForms, blockedToDelete);
 
   // Remove empty sections.
-  __weak PasswordsTableViewController* weakSelf = self;
+  __weak PasswordManagerViewController* weakSelf = self;
   [self.tableView
       performBatchUpdates:^{
-        PasswordsTableViewController* strongSelf = weakSelf;
+        PasswordManagerViewController* strongSelf = weakSelf;
         if (!strongSelf)
           return;
 
@@ -1784,7 +1783,7 @@
         }
       }
       completion:^(BOOL finished) {
-        PasswordsTableViewController* strongSelf = weakSelf;
+        PasswordManagerViewController* strongSelf = weakSelf;
         if (!strongSelf)
           return;
         // If both lists are empty, exit editing mode.
@@ -2106,7 +2105,7 @@
           l10n_util::GetNSString(IDS_IOS_EXPORT_PASSWORDS_PREPARING_ALERT_TITLE)
                        message:nil
                 preferredStyle:UIAlertControllerStyleAlert];
-  __weak PasswordsTableViewController* weakSelf = self;
+  __weak PasswordManagerViewController* weakSelf = self;
   UIAlertAction* cancelAction =
       [UIAlertAction actionWithTitle:l10n_util::GetNSString(
                                          IDS_IOS_EXPORT_PASSWORDS_CANCEL_BUTTON)
@@ -2186,7 +2185,7 @@
 
 - (void)presentViewController:(UIViewController*)viewController {
   if (_preparingPasswordsAlert.beingPresented) {
-    __weak PasswordsTableViewController* weakSelf = self;
+    __weak PasswordManagerViewController* weakSelf = self;
     [_preparingPasswordsAlert
         dismissViewControllerAnimated:YES
                            completion:^{
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h b/ios/chrome/browser/ui/settings/password/password_manager_view_controller_delegate.h
similarity index 83%
rename from ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h
rename to ios/chrome/browser/ui/settings/password/password_manager_view_controller_delegate.h
index 1d06f63..d9538b97 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/settings/password/password_manager_view_controller_delegate.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_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_DELEGATE_H_
 
 #import <Foundation/Foundation.h>
 
@@ -32,8 +32,8 @@
 struct PasswordForm;
 }
 
-// Delegate for `PasswordsTableViewController`.
-@protocol PasswordsTableViewControllerDelegate
+// Delegate for `PasswordManagerViewController`.
+@protocol PasswordManagerViewControllerDelegate
 
 // Deletes form with its duplicates.
 - (void)deletePasswordForms:
@@ -56,4 +56,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_manager_view_controller_presentation_delegate.h b/ios/chrome/browser/ui/settings/password/password_manager_view_controller_presentation_delegate.h
new file mode 100644
index 0000000..8abee6c5
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/password/password_manager_view_controller_presentation_delegate.h
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+// Presentation delegate for `PasswordManagerViewController`.
+@protocol PasswordManagerViewControllerPresentationDelegate
+
+// Called when `PasswordManagerViewController` is dismissed.
+- (void)PasswordManagerViewControllerDismissed;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_MANAGER_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_manager_view_controller_unittest.mm
similarity index 89%
rename from ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
rename to ios/chrome/browser/ui/settings/password/password_manager_view_controller_unittest.mm
index fc8d4ae..3674d92 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_manager_view_controller_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller.h"
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
@@ -54,16 +54,16 @@
 #endif
 
 using password_manager::InsecureType;
+using password_manager::MockBulkLeakCheckService;
 using password_manager::PasswordForm;
 using password_manager::TestPasswordStore;
-using password_manager::MockBulkLeakCheckService;
 using ::testing::Return;
 
 // Declaration to conformance to SavePasswordsConsumerDelegate and keep tests in
 // this file working.
-@interface PasswordsTableViewController (Test) <PasswordsConsumer,
-                                                UISearchBarDelegate,
-                                                UISearchControllerDelegate>
+@interface PasswordManagerViewController (Test) <PasswordsConsumer,
+                                                 UISearchBarDelegate,
+                                                 UISearchControllerDelegate>
 - (void)updateExportPasswordsButton;
 @end
 
@@ -89,9 +89,9 @@
   bool password_check_enabled;
 } PasswordCheckFeatureStatus;
 
-class PasswordsTableViewControllerTest : public ChromeTableViewControllerTest {
+class PasswordManagerViewControllerTest : public ChromeTableViewControllerTest {
  protected:
-  PasswordsTableViewControllerTest() = default;
+  PasswordManagerViewControllerTest() = default;
 
   void SetUp() override {
     ChromeTableViewControllerTest::SetUp();
@@ -126,8 +126,8 @@
                                          browserState)];
 
     // Inject some fake passwords to pass the loading state.
-    PasswordsTableViewController* passwords_controller =
-        static_cast<PasswordsTableViewController*>(controller());
+    PasswordManagerViewController* passwords_controller =
+        static_cast<PasswordManagerViewController*>(controller());
     passwords_controller.delegate = mediator_;
     mediator_.consumer = passwords_controller;
     [passwords_controller setPasswordsForms:{} blockedForms:{}];
@@ -173,12 +173,12 @@
 
   ChromeTableViewController* InstantiateController() override {
     return
-        [[PasswordsTableViewController alloc] initWithBrowser:browser_.get()];
+        [[PasswordManagerViewController alloc] initWithBrowser:browser_.get()];
   }
 
   void ChangePasswordCheckState(PasswordCheckUIState state) {
-    PasswordsTableViewController* passwords_controller =
-        static_cast<PasswordsTableViewController*>(controller());
+    PasswordManagerViewController* passwords_controller =
+        static_cast<PasswordManagerViewController*>(controller());
     NSInteger count = 0;
     for (const auto& signon_realm_forms : GetTestStore().stored_passwords()) {
       count += base::ranges::count_if(signon_realm_forms.second,
@@ -191,7 +191,7 @@
                  unmutedCompromisedPasswordsCount:count];
   }
 
-  // Adds a form to PasswordsTableViewController.
+  // Adds a form to PasswordManagerViewController.
   void AddPasswordForm(std::unique_ptr<password_manager::PasswordForm> form) {
     GetTestStore().AddLogin(*form);
     RunUntilIdle();
@@ -273,8 +273,8 @@
 
   // Deletes the item at (row, section) and wait util idle.
   void deleteItemAndWait(int section, int row) {
-    PasswordsTableViewController* passwords_controller =
-        static_cast<PasswordsTableViewController*>(controller());
+    PasswordManagerViewController* passwords_controller =
+        static_cast<PasswordManagerViewController*>(controller());
     [passwords_controller
         deleteItems:@[ [NSIndexPath indexPathForRow:row inSection:section] ]];
     RunUntilIdle();
@@ -295,8 +295,8 @@
 
   // Enables/Disables the edit mode based on `editing`.
   void SetEditing(bool editing) {
-    PasswordsTableViewController* passwords_controller =
-        static_cast<PasswordsTableViewController*>(controller());
+    PasswordManagerViewController* passwords_controller =
+        static_cast<PasswordManagerViewController*>(controller());
     [passwords_controller setEditing:editing animated:NO];
   }
 
@@ -311,13 +311,13 @@
 };
 
 // Tests default case has no saved sites and no blocked sites.
-TEST_F(PasswordsTableViewControllerTest, TestInitialization) {
+TEST_F(PasswordManagerViewControllerTest, TestInitialization) {
   CheckController();
   EXPECT_EQ(3 + SectionsOffset(), NumberOfSections());
 }
 
 // Tests adding one item in saved password section.
-TEST_F(PasswordsTableViewControllerTest, AddSavedPasswords) {
+TEST_F(PasswordManagerViewControllerTest, AddSavedPasswords) {
   AddSavedForm1();
 
   EXPECT_EQ(4 + SectionsOffset(), NumberOfSections());
@@ -326,7 +326,7 @@
 }
 
 // Tests adding one item in blocked password section.
-TEST_F(PasswordsTableViewControllerTest, AddBlockedPasswords) {
+TEST_F(PasswordManagerViewControllerTest, AddBlockedPasswords) {
   AddBlockedForm1();
 
   EXPECT_EQ(4 + SectionsOffset(), NumberOfSections());
@@ -336,7 +336,7 @@
 
 // Tests adding one item in saved password section, and two items in blocked
 // password section.
-TEST_F(PasswordsTableViewControllerTest, AddSavedAndBlocked) {
+TEST_F(PasswordManagerViewControllerTest, AddSavedAndBlocked) {
   AddSavedForm1();
   AddBlockedForm1();
   AddBlockedForm2();
@@ -353,7 +353,7 @@
 }
 
 // Tests the order in which the saved passwords are displayed.
-TEST_F(PasswordsTableViewControllerTest, TestSavedPasswordsOrder) {
+TEST_F(PasswordManagerViewControllerTest, TestSavedPasswordsOrder) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
       password_manager::features::kEnableFaviconForPasswords);
@@ -374,7 +374,7 @@
 }
 
 // Tests the order in which the blocked passwords are displayed.
-TEST_F(PasswordsTableViewControllerTest, TestBlockedPasswordsOrder) {
+TEST_F(PasswordManagerViewControllerTest, TestBlockedPasswordsOrder) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
       password_manager::features::kEnableFaviconForPasswords);
@@ -393,7 +393,7 @@
 // Tests the order in which the saved passwords are displayed.
 // TODO(crbug.com/1300569): Remove this when kEnableFaviconForPasswords flag is
 // removed.
-TEST_F(PasswordsTableViewControllerTest, TestSavedPasswordsOrderLegacy) {
+TEST_F(PasswordManagerViewControllerTest, TestSavedPasswordsOrderLegacy) {
   AddSavedForm2();
 
   CheckTextCellTextAndDetailText(
@@ -412,7 +412,7 @@
 // Tests the order in which the blocked passwords are displayed.
 // TODO(crbug.com/1300569): Remove this when kEnableFaviconForPasswords flag is
 // removed.
-TEST_F(PasswordsTableViewControllerTest, TestBlockedPasswordsOrderLegacy) {
+TEST_F(PasswordManagerViewControllerTest, TestBlockedPasswordsOrderLegacy) {
   AddBlockedForm2();
   CheckTextCellText(@"secret2.com",
                     GetSectionIndex(SectionIdentifierSavedPasswords), 0);
@@ -426,7 +426,7 @@
 
 // Tests displaying passwords in the saved passwords section when there are
 // duplicates in the password store.
-TEST_F(PasswordsTableViewControllerTest, AddSavedDuplicates) {
+TEST_F(PasswordManagerViewControllerTest, AddSavedDuplicates) {
   AddSavedForm1();
   AddSavedForm1();
 
@@ -437,7 +437,7 @@
 
 // Tests displaying passwords in the blocked passwords section when there
 // are duplicates in the password store.
-TEST_F(PasswordsTableViewControllerTest, AddBlockedDuplicates) {
+TEST_F(PasswordManagerViewControllerTest, AddBlockedDuplicates) {
   AddBlockedForm1();
   AddBlockedForm1();
 
@@ -447,7 +447,7 @@
 }
 
 // Tests deleting items from saved passwords and blocked passwords sections.
-TEST_F(PasswordsTableViewControllerTest, DeleteItems) {
+TEST_F(PasswordManagerViewControllerTest, DeleteItems) {
   AddSavedForm1();
   AddBlockedForm1();
   AddBlockedForm2();
@@ -474,7 +474,7 @@
 
 // Tests deleting items from saved passwords and blocked passwords sections
 // when there are duplicates in the store.
-TEST_F(PasswordsTableViewControllerTest, DeleteItemsWithDuplicates) {
+TEST_F(PasswordManagerViewControllerTest, DeleteItemsWithDuplicates) {
   AddSavedForm1();
   AddSavedForm1();
   AddBlockedForm1();
@@ -501,10 +501,10 @@
   EXPECT_EQ(4, NumberOfSections());
 }
 
-TEST_F(PasswordsTableViewControllerTest,
+TEST_F(PasswordManagerViewControllerTest,
        TestExportButtonDisabledNoSavedPasswords) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
   [passwords_controller updateExportPasswordsButton];
 
   TableViewDetailTextItem* exportButton =
@@ -525,10 +525,10 @@
               UIAccessibilityTraitNotEnabled);
 }
 
-TEST_F(PasswordsTableViewControllerTest,
+TEST_F(PasswordManagerViewControllerTest,
        TestExportButtonEnabledWithSavedPasswords) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
   AddSavedForm1();
   [passwords_controller updateExportPasswordsButton];
 
@@ -545,12 +545,13 @@
 }
 
 // Tests that adding "on device encryption" don’t break during search.
-TEST_F(PasswordsTableViewControllerTest, TestOnDeviceEncryptionWhileSearching) {
+TEST_F(PasswordManagerViewControllerTest,
+       TestOnDeviceEncryptionWhileSearching) {
   root_view_controller_ = [[UIViewController alloc] init];
   scoped_window_.Get().rootViewController = root_view_controller_;
 
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
 
   // Present the view controller.
   __block bool presentation_finished = NO;
@@ -590,9 +591,9 @@
 }
 
 // Tests that the "Export Passwords..." button is greyed out in edit mode.
-TEST_F(PasswordsTableViewControllerTest, TestExportButtonDisabledEditMode) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+TEST_F(PasswordManagerViewControllerTest, TestExportButtonDisabledEditMode) {
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
   AddSavedForm1();
   [passwords_controller updateExportPasswordsButton];
 
@@ -611,10 +612,10 @@
 
 // Tests that the "Export Passwords..." button is enabled after exiting
 // edit mode.
-TEST_F(PasswordsTableViewControllerTest,
+TEST_F(PasswordManagerViewControllerTest,
        TestExportButtonEnabledWhenEdittingFinished) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
   AddSavedForm1();
   [passwords_controller updateExportPasswordsButton];
 
@@ -633,10 +634,10 @@
 }
 
 // Tests that the "Check Now" button is greyed out in edit mode.
-TEST_F(PasswordsTableViewControllerTest,
+TEST_F(PasswordManagerViewControllerTest,
        TestCheckPasswordButtonDisabledEditMode) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
   AddSavedForm1();
 
   TableViewDetailTextItem* checkPasswordButton =
@@ -658,7 +659,7 @@
 }
 
 // Tests filtering of items.
-TEST_F(PasswordsTableViewControllerTest, FilterItems) {
+TEST_F(PasswordManagerViewControllerTest, FilterItems) {
   AddSavedForm1();
   AddSavedForm2();
   AddBlockedForm1();
@@ -666,8 +667,8 @@
 
   EXPECT_EQ(5 + SectionsOffset(), NumberOfSections());
 
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
   UISearchBar* bar =
       passwords_controller.navigationItem.searchController.searchBar;
 
@@ -719,7 +720,7 @@
 }
 
 // Test verifies disabled state of password check cell.
-TEST_F(PasswordsTableViewControllerTest, PasswordCheckStateDisabled) {
+TEST_F(PasswordManagerViewControllerTest, PasswordCheckStateDisabled) {
   ChangePasswordCheckState(PasswordCheckStateDisabled);
 
   CheckDetailItemTextWithIds(
@@ -738,7 +739,7 @@
 }
 
 // Test verifies default state of password check cell.
-TEST_F(PasswordsTableViewControllerTest, PasswordCheckStateDefault) {
+TEST_F(PasswordManagerViewControllerTest, PasswordCheckStateDefault) {
   ChangePasswordCheckState(PasswordCheckStateDefault);
 
   CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
@@ -759,7 +760,7 @@
 }
 
 // Test verifies safe state of password check cell.
-TEST_F(PasswordsTableViewControllerTest, PasswordCheckStateSafe) {
+TEST_F(PasswordManagerViewControllerTest, PasswordCheckStateSafe) {
   ChangePasswordCheckState(PasswordCheckStateSafe);
 
   CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
@@ -780,7 +781,7 @@
 }
 
 // Test verifies unsafe state of password check cell.
-TEST_F(PasswordsTableViewControllerTest, PasswordCheckStateUnSafe) {
+TEST_F(PasswordManagerViewControllerTest, PasswordCheckStateUnSafe) {
   AddSavedForm1(/*has_password_issues=*/true);
   ChangePasswordCheckState(PasswordCheckStateUnSafe);
 
@@ -802,7 +803,7 @@
 }
 
 // Test verifies running state of password check cell.
-TEST_F(PasswordsTableViewControllerTest, PasswordCheckStateRunning) {
+TEST_F(PasswordManagerViewControllerTest, PasswordCheckStateRunning) {
   ChangePasswordCheckState(PasswordCheckStateRunning);
 
   CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
@@ -823,7 +824,7 @@
 }
 
 // Test verifies error state of password check cell.
-TEST_F(PasswordsTableViewControllerTest, PasswordCheckStateError) {
+TEST_F(PasswordManagerViewControllerTest, PasswordCheckStateError) {
   ChangePasswordCheckState(PasswordCheckStateError);
 
   CheckTextCellTextWithId(IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON,
@@ -846,9 +847,9 @@
 }
 
 // Test verifies tapping start with no saved passwords has no effect.
-TEST_F(PasswordsTableViewControllerTest, DisabledPasswordCheck) {
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+TEST_F(PasswordManagerViewControllerTest, DisabledPasswordCheck) {
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
 
   EXPECT_CALL(GetMockPasswordCheckService(), CheckUsernamePasswordPairs)
       .Times(0);
@@ -863,12 +864,12 @@
 }
 
 // Test verifies tapping start triggers correct function in service.
-TEST_F(PasswordsTableViewControllerTest, StartPasswordCheck) {
+TEST_F(PasswordManagerViewControllerTest, StartPasswordCheck) {
   AddSavedForm1();
   RunUntilIdle();
 
-  PasswordsTableViewController* passwords_controller =
-      static_cast<PasswordsTableViewController*>(controller());
+  PasswordManagerViewController* passwords_controller =
+      static_cast<PasswordManagerViewController*>(controller());
 
   EXPECT_CALL(GetMockPasswordCheckService(), CheckUsernamePasswordPairs);
 
@@ -881,7 +882,7 @@
 }
 
 // Test verifies changes to the password store are reflected on UI.
-TEST_F(PasswordsTableViewControllerTest, PasswordStoreListener) {
+TEST_F(PasswordManagerViewControllerTest, PasswordStoreListener) {
   AddSavedForm1();
   EXPECT_EQ(1, NumberOfItemsInSection(
                    GetSectionIndex(SectionIdentifierSavedPasswords)));
diff --git a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
index 0c7e4fb..d064ef2 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
@@ -23,12 +23,12 @@
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller_presentation_delegate.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_mediator.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_settings_commands.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -41,11 +41,11 @@
     PasswordIssuesCoordinatorDelegate,
     PasswordsInOtherAppsCoordinatorDelegate,
     PasswordsSettingsCommands,
-    PasswordsTableViewControllerPresentationDelegate>
+    PasswordManagerViewControllerPresentationDelegate>
 
 // Main view controller for this coordinator.
 @property(nonatomic, strong)
-    PasswordsTableViewController* passwordsViewController;
+    PasswordManagerViewController* passwordsViewController;
 
 // Main mediator for this coordinator.
 @property(nonatomic, strong) PasswordsMediator* mediator;
@@ -124,7 +124,7 @@
       initWithSuccessfulReauthTimeAccessor:self.mediator];
 
   self.passwordsViewController =
-      [[PasswordsTableViewController alloc] initWithBrowser:self.browser];
+      [[PasswordManagerViewController alloc] initWithBrowser:self.browser];
 
   self.passwordsViewController.handler = self;
   self.passwordsViewController.delegate = self.mediator;
@@ -204,9 +204,9 @@
   [self.passwordsInOtherAppsCoordinator start];
 }
 
-#pragma mark - PasswordsTableViewControllerPresentationDelegate
+#pragma mark - PasswordManagerViewControllerPresentationDelegate
 
-- (void)passwordsTableViewControllerDismissed {
+- (void)PasswordManagerViewControllerDismissed {
   [self.delegate passwordsCoordinatorDidRemove:self];
 }
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.h b/ios/chrome/browser/ui/settings/password/passwords_mediator.h
index 88cfd1a8..2f6d35e6 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.h
@@ -10,7 +10,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/sync/sync_service_factory.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/settings/utils/password_auto_fill_status_observer.h"
 #import "ios/chrome/browser/ui/table_view/table_view_favicon_data_source.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
@@ -22,7 +22,7 @@
 
 // This mediator fetches and organises the passwords for its consumer.
 @interface PasswordsMediator : NSObject <PasswordAutoFillStatusObserver,
-                                         PasswordsTableViewControllerDelegate,
+                                         PasswordManagerViewControllerDelegate,
                                          SuccessfulReauthTimeAccessor,
                                          TableViewFaviconDataSource>
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
index 1cc1f64..3546c41 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
@@ -57,7 +57,7 @@
   std::unique_ptr<PasswordCheckObserverBridge> _passwordCheckObserver;
 
   // A helper object for passing data about saved passwords from a finished
-  // password store request to the PasswordsTableViewController.
+  // password store request to the PasswordManagerViewController.
   std::unique_ptr<SavedPasswordsPresenterObserverBridge>
       _passwordsPresenterObserver;
 
@@ -158,7 +158,7 @@
   _syncService = nullptr;
 }
 
-#pragma mark - PasswordsTableViewControllerDelegate
+#pragma mark - PasswordManagerViewControllerDelegate
 
 - (void)deletePasswordForms:
     (const std::vector<password_manager::PasswordForm>&)forms {
diff --git a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
index ff100d212..9261eef4 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
@@ -294,7 +294,8 @@
   // UIAccessibilityTraitButton.
   return grey_allOf(
       grey_accessibilityLabel(l10n_util::GetNSString(label)),
-      grey_not(grey_accessibilityTrait(UIAccessibilityTraitButton)), nil);
+      grey_not(grey_accessibilityTrait(UIAccessibilityTraitButton)),
+      grey_userInteractionEnabled(), nil);
 }
 
 // Returns matcher for the "Add Password" button located at the bottom of the
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h
deleted file mode 100644
index 028a27b..0000000
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_presentation_delegate.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
-
-#import <Foundation/Foundation.h>
-
-// Presentation delegate for `PasswordsTableViewController`.
-@protocol PasswordsTableViewControllerPresentationDelegate
-
-// Called when `PasswordsTableViewController` is dismissed.
-- (void)passwordsTableViewControllerDismissed;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm b/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm
index 350985e..56141c3 100644
--- a/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm
+++ b/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm
@@ -66,10 +66,10 @@
   void RegisterMessages() override;
 
   // Messages.
-  void OnEnableNotifyUIWithState(const base::ListValue* list);
-  void OnStartNetLog(const base::ListValue* list);
-  void OnStopNetLog(const base::ListValue* list);
-  void OnSendNetLog(const base::ListValue* list);
+  void OnEnableNotifyUIWithState(const base::Value::List& list);
+  void OnStartNetLog(const base::Value::List& list);
+  void OnStopNetLog(const base::Value::List& list);
+  void OnSendNetLog(const base::Value::List& list);
 
   // net_log::NetExportFileWriter::StateObserver implementation.
   void OnNewState(const base::DictionaryValue& state) override;
@@ -105,26 +105,26 @@
 void NetExportMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       net_log::kEnableNotifyUIWithStateHandler,
       base::BindRepeating(&NetExportMessageHandler::OnEnableNotifyUIWithState,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       net_log::kStartNetLogHandler,
       base::BindRepeating(&NetExportMessageHandler::OnStartNetLog,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       net_log::kStopNetLogHandler,
       base::BindRepeating(&NetExportMessageHandler::OnStopNetLog,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       net_log::kSendNetLogHandler,
       base::BindRepeating(&NetExportMessageHandler::OnSendNetLog,
                           base::Unretained(this)));
 }
 
 void NetExportMessageHandler::OnEnableNotifyUIWithState(
-    const base::ListValue* list) {
+    const base::Value::List& list) {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   if (!state_observation_manager_.IsObserving()) {
     state_observation_manager_.Observe(file_writer_);
@@ -132,11 +132,9 @@
   NotifyUIWithState(file_writer_->GetState());
 }
 
-void NetExportMessageHandler::OnStartNetLog(const base::ListValue* list) {
+void NetExportMessageHandler::OnStartNetLog(const base::Value::List& params) {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
 
-  base::Value::ConstListView params = list->GetListDeprecated();
-
   // Determine the capture mode.
   net::NetLogCaptureMode capture_mode = net::NetLogCaptureMode::kDefault;
   if (params.size() > 0 && params[0].is_string()) {
@@ -160,12 +158,12 @@
       GetChannelString(), GetApplicationContext()->GetSystemNetworkContext());
 }
 
-void NetExportMessageHandler::OnStopNetLog(const base::ListValue* list) {
+void NetExportMessageHandler::OnStopNetLog(const base::Value::List& list) {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   file_writer_->StopNetLog();
 }
 
-void NetExportMessageHandler::OnSendNetLog(const base::ListValue* list) {
+void NetExportMessageHandler::OnSendNetLog(const base::Value::List& list) {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   file_writer_->GetFilePathToCompletedLog(base::BindOnce(
       &NetExportMessageHandler::SendEmail, base::Unretained(this)));
diff --git a/ios/chrome/test/app/password_test_util.mm b/ios/chrome/test/app/password_test_util.mm
index 78368d00..64aa691 100644
--- a/ios/chrome/test/app/password_test_util.mm
+++ b/ios/chrome/test/app/password_test_util.mm
@@ -6,7 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h"
-#import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/util/top_view_controller.h"
 
@@ -72,10 +72,10 @@
   SettingsNavigationController* settings_navigation_controller =
       base::mac::ObjCCastStrict<SettingsNavigationController>(
           top_view_controller::TopPresentedViewController());
-  PasswordsTableViewController* passwords_table_view_controller =
-      base::mac::ObjCCastStrict<PasswordsTableViewController>(
+  PasswordManagerViewController* password_manager_view_controller =
+      base::mac::ObjCCastStrict<PasswordManagerViewController>(
           settings_navigation_controller.topViewController);
-  passwords_table_view_controller.reauthenticationModule =
+  password_manager_view_controller.reauthenticationModule =
       mock_reauthentication_module;
   return mock_reauthentication_module;
 }
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
index 608aa29..b7d3f22d 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -28,17 +28,17 @@
 #define EarlGrey [self earlGrey]
 #pragma clang diagnostic pop
 
+using base::test::ios::kWaitForUIElementTimeout;
+using base::test::ios::WaitUntilConditionOrTimeout;
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::ClearAutofillButton;
 using chrome_test_util::ClearBrowsingDataButton;
 using chrome_test_util::ClearBrowsingDataView;
 using chrome_test_util::ClearSavedPasswordsButton;
 using chrome_test_util::ConfirmClearBrowsingDataButton;
+using chrome_test_util::SettingsDestinationButton;
 using chrome_test_util::SettingsMenuBackButton;
-using chrome_test_util::SettingsMenuButton;
 using chrome_test_util::ToolsMenuView;
-using base::test::ios::kWaitForUIElementTimeout;
-using base::test::ios::WaitUntilConditionOrTimeout;
 
 namespace {
 
@@ -168,12 +168,12 @@
 
 - (void)openSettingsMenu {
   [self openToolsMenu];
-  [self tapToolsMenuButton:SettingsMenuButton()];
+  [self tapToolsMenuButton:SettingsDestinationButton()];
 }
 
 - (void)openSettingsMenuInWindowWithNumber:(int)windowNumber {
   [self openToolsMenuInWindowWithNumber:windowNumber];
-  [self tapToolsMenuButton:SettingsMenuButton()];
+  [self tapToolsMenuButton:SettingsDestinationButton()];
 }
 
 - (void)openNewTabMenu {
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 6dd3dde4..8711486 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -212,9 +212,6 @@
 // Returns a matcher for the clear browsing data action sheet item.
 id<GREYMatcher> ConfirmClearBrowsingDataButton();
 
-// Returns a matcher for the settings button in the tools menu.
-id<GREYMatcher> SettingsMenuButton();
-
 // Returns a matcher for the "Done" button in the settings' navigation bar.
 id<GREYMatcher> SettingsDoneButton();
 
@@ -380,15 +377,6 @@
 // Returns a matcher for the New Window button on the Tools menu.
 id<GREYMatcher> OpenNewWindowMenuButton();
 
-// Returns a matcher for the reading list on the Tools menu.
-id<GREYMatcher> ReadingListMenuButton();
-
-// Returns a matcher for the bookmarks button on the Tools menu.
-id<GREYMatcher> BookmarksMenuButton();
-
-// Returns a matcher for the recent tabs button on the Tools menu.
-id<GREYMatcher> RecentTabsMenuButton();
-
 // Returns a matcher for the system selection callout.
 id<GREYMatcher> SystemSelectionCallout();
 
@@ -624,6 +612,34 @@
 // Returns a matcher for the button to accept the generated password.
 id<GREYMatcher> UseSuggestedPasswordMatcher();
 
+#pragma mark - Overflow Menu Destinations
+
+// Returns a matcher for the bookmarks destination button in the overflow menu.
+id<GREYMatcher> BookmarksDestinationButton();
+
+// Returns a matcher for the history destination button in the overflow menu.
+id<GREYMatcher> HistoryDestinationButton();
+
+// Returns a matcher for the reading list destination button in the overflow
+// menu.
+id<GREYMatcher> ReadingListDestinationButton();
+
+// Returns a matcher for the passwords destination button in the overflow menu.
+id<GREYMatcher> PasswordsDestinationButton();
+
+// Returns a matcher for the downloads destination button in the overflow menu.
+id<GREYMatcher> DownloadsDestinationButton();
+
+// Returns a matcher for the recent tabs destination button in the overflow
+// menu.
+id<GREYMatcher> RecentTabsDestinationButton();
+
+// Returns a matcher for the site info destination button in the overflow menu.
+id<GREYMatcher> SiteInfoDestinationButton();
+
+// Returns a matcher for the settings destination button in the overflow menu.
+id<GREYMatcher> SettingsDestinationButton();
+
 #pragma mark - Tab Grid Edit Mode
 
 // Returns a matcher for the button to open the context menu for edit actions.
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index fbe0d50..649154c3 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -276,10 +276,6 @@
   return [ChromeMatchersAppInterface confirmClearBrowsingDataButton];
 }
 
-id<GREYMatcher> SettingsMenuButton() {
-  return [ChromeMatchersAppInterface settingsMenuButton];
-}
-
 id<GREYMatcher> SettingsDoneButton() {
   return [ChromeMatchersAppInterface settingsDoneButton];
 }
@@ -477,18 +473,6 @@
   return [ChromeMatchersAppInterface openNewWindowMenuButton];
 }
 
-id<GREYMatcher> ReadingListMenuButton() {
-  return [ChromeMatchersAppInterface readingListMenuButton];
-}
-
-id<GREYMatcher> BookmarksMenuButton() {
-  return [ChromeMatchersAppInterface bookmarksMenuButton];
-}
-
-id<GREYMatcher> RecentTabsMenuButton() {
-  return [ChromeMatchersAppInterface recentTabsMenuButton];
-}
-
 id<GREYMatcher> SystemSelectionCallout() {
   return [ChromeMatchersAppInterface systemSelectionCallout];
 }
@@ -680,6 +664,40 @@
   return [ChromeMatchersAppInterface settingsToolbarAddButton];
 }
 
+#pragma mark - Overflow Menu Destinations
+
+id<GREYMatcher> BookmarksDestinationButton() {
+  return [ChromeMatchersAppInterface bookmarksDestinationButton];
+}
+
+id<GREYMatcher> HistoryDestinationButton() {
+  return [ChromeMatchersAppInterface historyDestinationButton];
+}
+
+id<GREYMatcher> ReadingListDestinationButton() {
+  return [ChromeMatchersAppInterface readingListDestinationButton];
+}
+
+id<GREYMatcher> PasswordsDestinationButton() {
+  return [ChromeMatchersAppInterface passwordsDestinationButton];
+}
+
+id<GREYMatcher> DownloadsDestinationButton() {
+  return [ChromeMatchersAppInterface downloadsDestinationButton];
+}
+
+id<GREYMatcher> RecentTabsDestinationButton() {
+  return [ChromeMatchersAppInterface recentTabsDestinationButton];
+}
+
+id<GREYMatcher> SiteInfoDestinationButton() {
+  return [ChromeMatchersAppInterface siteInfoDestinationButton];
+}
+
+id<GREYMatcher> SettingsDestinationButton() {
+  return [ChromeMatchersAppInterface settingsDestinationButton];
+}
+
 #pragma mark - Manual Fallback
 
 id<GREYMatcher> ManualFallbackFormSuggestionViewMatcher() {
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index 8ec1ed2..062ffeda 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -198,9 +198,6 @@
 // Matcher for the clear browsing data action sheet item.
 + (id<GREYMatcher>)confirmClearBrowsingDataButton;
 
-// Returns matcher for the settings button in the tools menu.
-+ (id<GREYMatcher>)settingsMenuButton;
-
 // Returns matcher for the "Done" button in the settings' navigation bar.
 + (id<GREYMatcher>)settingsDoneButton;
 
@@ -369,15 +366,6 @@
 // Returns matcher for the New Window button on the Tools menu.
 + (id<GREYMatcher>)openNewWindowMenuButton;
 
-// Returns matcher for the reading list button on the Tools menu.
-+ (id<GREYMatcher>)readingListMenuButton;
-
-// Returns matcher for the bookmarks button on the Tools menu.
-+ (id<GREYMatcher>)bookmarksMenuButton;
-
-// Returns matcher for the recent tabs button on the Tools menu.
-+ (id<GREYMatcher>)recentTabsMenuButton;
-
 // Returns matcher for the system selection callout.
 + (id<GREYMatcher>)systemSelectionCallout;
 
@@ -536,6 +524,40 @@
 // Returns a matcher to the add button in the toolbar of the settings view.
 + (id<GREYMatcher>)settingsToolbarAddButton;
 
+#pragma mark - Overflow Menu Destinations
+
+// Returns matcher for the bookmarks destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)bookmarksDestinationButton;
+
+// Returns matcher for the history destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)historyDestinationButton;
+
+// Returns matcher for the passwords destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)passwordsDestinationButton;
+
+// Returns matcher for the reading list destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)readingListDestinationButton;
+
+// Returns matcher for the recent tabs destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)recentTabsDestinationButton;
+
+// Returns matcher for the settings destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)settingsDestinationButton;
+
+// Returns matcher for the site info destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)siteInfoDestinationButton;
+
+// Returns matcher for the downloads destination button in the overflow menu
+// carousel.
++ (id<GREYMatcher>)downloadsDestinationButton;
+
 #pragma mark - Manual Fallback
 
 // Returns a matcher for the scroll view in keyboard accessory bar.
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 7202c60..74d2e80 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -507,10 +507,6 @@
       grey_userInteractionEnabled(), nil);
 }
 
-+ (id<GREYMatcher>)settingsMenuButton {
-  return grey_accessibilityID(kToolsMenuSettingsId);
-}
-
 + (id<GREYMatcher>)settingsDoneButton {
   return grey_accessibilityID(kSettingsDoneButtonId);
 }
@@ -793,18 +789,6 @@
   return grey_accessibilityID(kToolsMenuNewWindowId);
 }
 
-+ (id<GREYMatcher>)readingListMenuButton {
-  return grey_accessibilityID(kToolsMenuReadingListId);
-}
-
-+ (id<GREYMatcher>)bookmarksMenuButton {
-  return grey_accessibilityID(kToolsMenuBookmarksId);
-}
-
-+ (id<GREYMatcher>)recentTabsMenuButton {
-  return grey_accessibilityID(kToolsMenuOtherDevicesId);
-}
-
 + (id<GREYMatcher>)systemSelectionCallout {
   if (@available(iOS 16.0, *)) {
     return grey_kindOfClass(NSClassFromString(@"_UIEditMenuListViewCell"));
@@ -1068,6 +1052,40 @@
   return grey_accessibilityID(kSettingsToolbarAddButtonId);
 }
 
+#pragma mark - Overflow Menu Destinations
+
++ (id<GREYMatcher>)bookmarksDestinationButton {
+  return grey_accessibilityID(kToolsMenuBookmarksId);
+}
+
++ (id<GREYMatcher>)downloadsDestinationButton {
+  return grey_accessibilityID(kToolsMenuDownloadsId);
+}
+
++ (id<GREYMatcher>)historyDestinationButton {
+  return grey_accessibilityID(kToolsMenuHistoryId);
+}
+
++ (id<GREYMatcher>)passwordsDestinationButton {
+  return grey_accessibilityID(kToolsMenuPasswordsId);
+}
+
++ (id<GREYMatcher>)readingListDestinationButton {
+  return grey_accessibilityID(kToolsMenuReadingListId);
+}
+
++ (id<GREYMatcher>)recentTabsDestinationButton {
+  return grey_accessibilityID(kToolsMenuOtherDevicesId);
+}
+
++ (id<GREYMatcher>)settingsDestinationButton {
+  return grey_accessibilityID(kToolsMenuSettingsId);
+}
+
++ (id<GREYMatcher>)siteInfoDestinationButton {
+  return grey_accessibilityID(kToolsMenuSiteInformation);
+}
+
 #pragma mark - Manual Fallback
 
 + (id<GREYMatcher>)manualFallbackFormSuggestionViewMatcher {
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 3d42dd3..a8bd167 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 @@
-3918c58906f3b29b2f039c5ad1261a61cc812850
\ No newline at end of file
+b3be12d1dc7687a0918c420e60105a939d810323
\ 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 389cd02..8bc33cd 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 @@
-05585db86d7ebdccf0b37e769d159d618154fa3d
\ No newline at end of file
+0c641280e4efe1b832246f1c058acfedf4dfde21
\ 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 cd5a504a..38857e9d 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 @@
-43ca43eaf2bfb0d54475136fdfac95405d0be29a
\ No newline at end of file
+f7cafb609bfda98f82fa3c7ff08a01f7be9cde41
\ 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 26dd7d84..33f4cd4 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 @@
-a44812257a33aa9cfbd0c60c3cfc78043c104369
\ No newline at end of file
+730abc018dfb9e451c6d0ecc86b5c373a810116e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 054f6f5b..5903037 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-401ee5a99d4a5f25a8187981f3aa3460fd1e2d21
\ No newline at end of file
+bd7ec7ed6038a31d6d2ccd4e65e437996f56be76
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 85d95dacf..48dbbe2 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-7a2f7c208252e8f106e06d8f7420877197c72c3d
\ No newline at end of file
+d3505cc5cf770c62c0c05fae40a7cba3917efbd1
\ 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 49cd85df..f9c0a3e 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 @@
-a698e963e21147ecec98633d9130799e83533308
\ No newline at end of file
+7dd7eba0aaf693ec6671807d36cedb1f2f93eee1
\ 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 d6c7bddc..c0d370a 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 @@
-48049c7a0102cd1894636629087e257f1c7a02b0
\ No newline at end of file
+57fd4ef18bda74e8627dd7fbcf26e7574a56689b
\ 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 ad4557ac9..b6c9b85 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 @@
-474fc1622c4ea789ee2a1a8ad2a8f2ee1b90ad85
\ No newline at end of file
+8dcee02f88c8aa2eb7d30ac1626f25d0f7a4ad1a
\ 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 e1072896..65cf4e4 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 @@
-e435132e3288565833d3ad03e780c7236aa829be
\ No newline at end of file
+e881e266d8d0ad69fbc17c28e7d211ad3b32caf1
\ No newline at end of file
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index 43a6e646..27faa8a6 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -1598,6 +1598,13 @@
   cflags = [ "-Wno-nullability-completeness" ]
 }
 
+# TODO(crbug.com/1339746): MDCNavigationBar.h has conflicting deprecation
+# specification, causing compilation to fail. Disable the warning until this
+# is fixed in MDC. Remove this once fixed.
+config("disable_availability") {
+  cflags = [ "-Wno-availability" ]
+}
+
 ios_framework_bundle("material_components_ios") {
   sources = _mdc_sources
 
@@ -1640,6 +1647,11 @@
     # nullability specification, causing compilation to fail. Disable
     # the warning until this is fixed in MDC. Remove this once fixed.
     ":disable_nullability_completeness",
+
+    # TODO(crbug.com/1339746): MDCNavigationBar.h has conflicting deprecation
+    # specification, causing compilation to fail. Disable the warning until this
+    # is fixed in MDC. Remove this once fixed.
+    ":disable_availability",
   ]
 }
 
diff --git a/ios/web/js_messaging/BUILD.gn b/ios/web/js_messaging/BUILD.gn
index 0be0e53..0181a9f 100644
--- a/ios/web/js_messaging/BUILD.gn
+++ b/ios/web/js_messaging/BUILD.gn
@@ -10,6 +10,7 @@
   deps = [
     ":frame_listeners_js",
     ":java_script_content_world_header",
+    ":java_script_feature_manager_header",
     ":scoped_wk_script_message_handler",
     ":setup_frame_js",
     ":web_frames_manager_impl_header",
@@ -58,7 +59,10 @@
 
 source_set("java_script_feature") {
   configs += [ "//build/config/compiler:enable_arc" ]
-  public_deps = [ ":java_script_content_world_header" ]
+  public_deps = [
+    ":java_script_content_world_header",
+    ":java_script_feature_manager_header",
+  ]
   deps = [
     ":js_messaging",
     ":scoped_wk_script_message_handler",
@@ -73,7 +77,6 @@
   sources = [
     "java_script_content_world.mm",
     "java_script_feature.mm",
-    "java_script_feature_manager.h",
     "java_script_feature_manager.mm",
     "script_message.mm",
   ]
@@ -91,6 +94,17 @@
   sources = [ "java_script_content_world.h" ]
 }
 
+source_set("java_script_feature_manager_header") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":java_script_content_world_header",
+    "//base",
+  ]
+
+  sources = [ "java_script_feature_manager.h" ]
+}
+
 source_set("java_script_feature_util") {
   configs += [ "//build/config/compiler:enable_arc" ]
 
diff --git a/ios/web/js_messaging/java_script_content_world.h b/ios/web/js_messaging/java_script_content_world.h
index 6a9c1bf..09385b7 100644
--- a/ios/web/js_messaging/java_script_content_world.h
+++ b/ios/web/js_messaging/java_script_content_world.h
@@ -33,10 +33,6 @@
   ~JavaScriptContentWorld();
   JavaScriptContentWorld(const JavaScriptContentWorld&) = delete;
 
-  // Creates a content world for features which will interact with the page
-  // content world shared by the webpage's JavaScript.
-  explicit JavaScriptContentWorld(BrowserState* browser_state);
-
   // Creates a content world for features which will interact with the given
   // |content_world|.
   JavaScriptContentWorld(BrowserState* browser_state,
diff --git a/ios/web/js_messaging/java_script_content_world.mm b/ios/web/js_messaging/java_script_content_world.mm
index d103f7e3..d59ba84 100644
--- a/ios/web/js_messaging/java_script_content_world.mm
+++ b/ios/web/js_messaging/java_script_content_world.mm
@@ -53,11 +53,6 @@
 
 }  // namespace
 
-JavaScriptContentWorld::JavaScriptContentWorld(BrowserState* browser_state)
-    : browser_state_(browser_state),
-      user_content_controller_(GetUserContentController(browser_state)),
-      weak_factory_(this) {}
-
 #if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
 JavaScriptContentWorld::JavaScriptContentWorld(BrowserState* browser_state,
                                                WKContentWorld* content_world)
diff --git a/ios/web/js_messaging/java_script_content_world_unittest.mm b/ios/web/js_messaging/java_script_content_world_unittest.mm
index f147c32..c6b3df4 100644
--- a/ios/web/js_messaging/java_script_content_world_unittest.mm
+++ b/ios/web/js_messaging/java_script_content_world_unittest.mm
@@ -30,13 +30,14 @@
       [[user_content_controller userScripts] count];
   ASSERT_GT(initial_scripts_count, 0ul);
 
-  web::JavaScriptContentWorld world(GetBrowserState());
+  web::JavaScriptContentWorld world(GetBrowserState(),
+                                    WKContentWorld.pageWorld);
 
   FakeJavaScriptFeature feature(
       JavaScriptFeature::ContentWorld::kAnyContentWorld);
   world.AddFeature(&feature);
   EXPECT_TRUE(world.HasFeature(&feature));
-  EXPECT_FALSE(world.GetWKContentWorld());
+  EXPECT_EQ(WKContentWorld.pageWorld, world.GetWKContentWorld());
 
   unsigned long scripts_count = [[user_content_controller userScripts] count];
   ASSERT_GT(scripts_count, initial_scripts_count);
@@ -54,14 +55,14 @@
   ASSERT_GT(initial_scripts_count, 0ul);
 
   web::JavaScriptContentWorld world(GetBrowserState(),
-                                    [WKContentWorld defaultClientWorld]);
+                                    WKContentWorld.defaultClientWorld);
 
   FakeJavaScriptFeature feature(
       JavaScriptFeature::ContentWorld::kAnyContentWorld);
   world.AddFeature(&feature);
   EXPECT_TRUE(world.HasFeature(&feature));
 
-  EXPECT_EQ([WKContentWorld defaultClientWorld], world.GetWKContentWorld());
+  EXPECT_EQ(WKContentWorld.defaultClientWorld, world.GetWKContentWorld());
 
   unsigned long scripts_count = [[user_content_controller userScripts] count];
   ASSERT_GT(scripts_count, initial_scripts_count);
@@ -79,14 +80,14 @@
   ASSERT_GT(initial_scripts_count, 0ul);
 
   web::JavaScriptContentWorld world(GetBrowserState(),
-                                    [WKContentWorld defaultClientWorld]);
+                                    WKContentWorld.defaultClientWorld);
 
   FakeJavaScriptFeature feature(
       JavaScriptFeature::ContentWorld::kIsolatedWorldOnly);
   world.AddFeature(&feature);
   EXPECT_TRUE(world.HasFeature(&feature));
 
-  EXPECT_EQ([WKContentWorld defaultClientWorld], world.GetWKContentWorld());
+  EXPECT_EQ(WKContentWorld.defaultClientWorld, world.GetWKContentWorld());
 
   unsigned long scripts_count = [[user_content_controller userScripts] count];
   ASSERT_GT(scripts_count, initial_scripts_count);
@@ -96,7 +97,7 @@
 // content world triggers a DCHECK.
 TEST_F(JavaScriptContentWorldTest, AddIsolatedWorldFeatureToPageWorld) {
   web::JavaScriptContentWorld world(GetBrowserState(),
-                                    [WKContentWorld pageWorld]);
+                                    WKContentWorld.pageWorld);
   FakeJavaScriptFeature feature(
       JavaScriptFeature::ContentWorld::kIsolatedWorldOnly);
 
diff --git a/ios/web/js_messaging/java_script_feature_manager.h b/ios/web/js_messaging/java_script_feature_manager.h
index e8bb8d8..b76c8a0f 100644
--- a/ios/web/js_messaging/java_script_feature_manager.h
+++ b/ios/web/js_messaging/java_script_feature_manager.h
@@ -32,6 +32,13 @@
   static JavaScriptFeatureManager* FromBrowserState(
       BrowserState* browser_state);
 
+  // Returns the JavaScriptContentWorld for the page content world associated
+  // with |browser_state|. If a JavaScriptFeatureManager does not already exist,
+  // one will be created and associated with |browser_state|. |browser_state|
+  // must not be null.
+  static JavaScriptContentWorld* GetPageContentWorldForBrowserState(
+      BrowserState* browser_state);
+
   // Configures |features| on |user_content_controller_| by adding user scripts
   // and script message handlers.
   // NOTE: |page_content_world_| and |isolated_world_| will be recreated.
diff --git a/ios/web/js_messaging/java_script_feature_manager.mm b/ios/web/js_messaging/java_script_feature_manager.mm
index 3effbbb..c5b7eaa 100644
--- a/ios/web/js_messaging/java_script_feature_manager.mm
+++ b/ios/web/js_messaging/java_script_feature_manager.mm
@@ -58,10 +58,18 @@
   return feature_manager;
 }
 
+JavaScriptContentWorld*
+JavaScriptFeatureManager::GetPageContentWorldForBrowserState(
+    BrowserState* browser_state) {
+  DCHECK(browser_state);
+  JavaScriptFeatureManager* feature_manager = FromBrowserState(browser_state);
+  return feature_manager->page_content_world_.get();
+}
+
 void JavaScriptFeatureManager::ConfigureFeatures(
     std::vector<JavaScriptFeature*> features) {
-  page_content_world_ =
-      std::make_unique<JavaScriptContentWorld>(browser_state_);
+  page_content_world_ = std::make_unique<JavaScriptContentWorld>(
+      browser_state_, WKContentWorld.pageWorld);
   AddSharedCommonFeatures(page_content_world_.get());
 
   isolated_world_ = std::make_unique<JavaScriptContentWorld>(
diff --git a/ios/web/js_messaging/web_frame_impl.mm b/ios/web/js_messaging/web_frame_impl.mm
index 10bc355..34f8e8f 100644
--- a/ios/web/js_messaging/web_frame_impl.mm
+++ b/ios/web/js_messaging/web_frame_impl.mm
@@ -20,6 +20,7 @@
 #include "crypto/aead.h"
 #include "crypto/random.h"
 #import "ios/web/js_messaging/java_script_content_world.h"
+#import "ios/web/js_messaging/java_script_feature_manager.h"
 #import "ios/web/js_messaging/web_view_js_utils.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
@@ -114,7 +115,7 @@
   // because calling the function directly on the webstate with
   // |ExecuteJavaScript| is secure. However, iframes require an encryption key
   // in order to securely pass the function name and parameters to the frame.
-  return is_main_frame_ || frame_key_;
+  return is_main_frame_ || frame_key_ || frame_info_;
 }
 
 BrowserState* WebFrameImpl::GetBrowserState() {
@@ -173,6 +174,11 @@
                                      reply_with_result);
   }
 
+  // There should always be a content_world now and
+  // `__gCrWeb.message.routeMessage` calls shouldn't be necessary.
+  // TODO(crbug.com/1339441): Remove custom iFrame messaging system.
+  NOTREACHED();
+
   base::Value message_payload(base::Value::Type::DICTIONARY);
   message_payload.SetKey("messageId", base::Value(message_id));
   message_payload.SetKey("replyWithResult", base::Value(reply_with_result));
@@ -203,8 +209,11 @@
 bool WebFrameImpl::CallJavaScriptFunction(
     const std::string& name,
     const std::vector<base::Value>& parameters) {
-  return CallJavaScriptFunctionInContentWorld(name, parameters,
-                                              /*content_world=*/nullptr,
+  JavaScriptContentWorld* content_world =
+      JavaScriptFeatureManager::GetPageContentWorldForBrowserState(
+          GetBrowserState());
+
+  return CallJavaScriptFunctionInContentWorld(name, parameters, content_world,
                                               /*reply_with_result=*/false);
 }
 
@@ -221,8 +230,10 @@
     const std::vector<base::Value>& parameters,
     base::OnceCallback<void(const base::Value*)> callback,
     base::TimeDelta timeout) {
-  return CallJavaScriptFunctionInContentWorld(name, parameters,
-                                              /*content_world=*/nullptr,
+  JavaScriptContentWorld* content_world =
+      JavaScriptFeatureManager::GetPageContentWorldForBrowserState(
+          GetBrowserState());
+  return CallJavaScriptFunctionInContentWorld(name, parameters, content_world,
                                               std::move(callback), timeout);
 }
 
diff --git a/ios/web/js_messaging/web_frame_impl_unittest.mm b/ios/web/js_messaging/web_frame_impl_unittest.mm
index 9db06c7..d6fe93a 100644
--- a/ios/web/js_messaging/web_frame_impl_unittest.mm
+++ b/ios/web/js_messaging/web_frame_impl_unittest.mm
@@ -41,51 +41,6 @@
                                       decoded_frame_key_string);
 }
 
-struct RouteMessageParameters {
-  NSString* encoded_message_payload = nil;
-  NSString* encoded_message_iv = nil;
-  NSString* encoded_function_payload = nil;
-  NSString* encoded_function_iv = nil;
-  NSString* frame_id = nil;
-};
-
-RouteMessageParameters ParametersFromFunctionCallString(
-    NSString* function_call) {
-  NSRange parameters_start = [function_call rangeOfString:@"("];
-  NSRange parameters_end = [function_call rangeOfString:@")"];
-  NSMutableString* parameter_string = [[function_call
-      substringWithRange:NSMakeRange(parameters_start.location + 1,
-                                     parameters_end.location -
-                                         parameters_start.location - 1)]
-      mutableCopy];
-  // Create array string and replace single quotes with double quotes in
-  // preparation for JSON serialization.
-  [parameter_string insertString:@"[" atIndex:0];
-  [parameter_string appendString:@"]"];
-  NSString* final_string =
-      [parameter_string stringByReplacingOccurrencesOfString:@"'"
-                                                  withString:@"\""];
-
-  NSData* data = [final_string dataUsingEncoding:NSUTF8StringEncoding];
-  NSError* error = nil;
-  NSArray* jsonArray =
-      [NSJSONSerialization JSONObjectWithData:data
-                                      options:NSJSONReadingMutableContainers |
-                                              NSJSONReadingMutableLeaves
-                                        error:&error];
-
-  RouteMessageParameters parsed_params;
-  if (jsonArray.count == 3 && !error) {
-    parsed_params.encoded_message_iv = jsonArray[0][@"iv"];
-    parsed_params.encoded_message_payload = jsonArray[0][@"payload"];
-    parsed_params.encoded_function_iv = jsonArray[1][@"iv"];
-    parsed_params.encoded_function_payload = jsonArray[1][@"payload"];
-    parsed_params.frame_id = jsonArray[2];
-  }
-
-  return parsed_params;
-}
-
 }  // namespace
 
 namespace web {
@@ -123,22 +78,6 @@
   EXPECT_EQ(kFrameId, web_frame.GetFrameId());
 }
 
-// Tests creation of a WebFrame for a frame which is not the main frame without
-// an encryption key.
-TEST_F(WebFrameImplTest, CreateWebFrameForIFrame) {
-  FakeWebState fake_web_state;
-  GURL security_origin;
-  WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
-                         /*is_main_frame=*/false, security_origin,
-                         &fake_web_state);
-
-  EXPECT_EQ(&fake_web_state, web_frame.GetWebState());
-  EXPECT_FALSE(web_frame.IsMainFrame());
-  EXPECT_FALSE(web_frame.CanCallJavaScriptFunction());
-  EXPECT_EQ(security_origin, web_frame.GetSecurityOrigin());
-  EXPECT_EQ(kFrameId, web_frame.GetFrameId());
-}
-
 // Tests creation of a WebFrame for a frame which is not the main frame with an
 // encryption key.
 TEST_F(WebFrameImplTest, CreateWebFrameForIFrameWithKey) {
@@ -156,250 +95,11 @@
   EXPECT_EQ(kFrameId, web_frame.GetFrameId());
 }
 
-// Tests that |CallJavaScriptFunction| encrypts the message and passes it to
-// __gCrWeb.message.routeMessage in the main frame.
-TEST_F(WebFrameImplTest, CallJavaScriptFunction) {
-  FakeWebState fake_web_state;
-  GURL security_origin;
-  WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
-                         /*is_main_frame=*/false, security_origin,
-                         &fake_web_state);
-  web_frame.SetEncryptionKey(CreateKey());
-
-  std::vector<base::Value> function_params;
-  function_params.push_back(base::Value("plaintextParam"));
-  EXPECT_TRUE(
-      web_frame.CallJavaScriptFunction("functionName", function_params));
-
-  NSString* last_script =
-      base::SysUTF16ToNSString(fake_web_state.GetLastExecutedJavascript());
-  EXPECT_TRUE([last_script hasPrefix:@"__gCrWeb.message.routeMessage"]);
-  // Verify the message does not contain the plaintext function name or
-  // parameters.
-  EXPECT_FALSE([last_script containsString:@"functionName"]);
-  EXPECT_FALSE([last_script containsString:@"plaintextParam"]);
-
-  RouteMessageParameters params = ParametersFromFunctionCallString(last_script);
-
-  // Verify that the message and function payload are properly base64 encoded
-  // strings.
-  std::string decoded_function_payload;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_function_payload),
-      &decoded_function_payload));
-  std::string decoded_message_payload;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_message_payload),
-      &decoded_message_payload));
-  // Verify the function does not contain the plaintext function name or
-  // parameters.
-  EXPECT_FALSE([base::SysUTF8ToNSString(decoded_function_payload)
-      containsString:@"functionName"]);
-  EXPECT_FALSE([base::SysUTF8ToNSString(decoded_function_payload)
-      containsString:@"plaintextParam"]);
-
-  // Verify that the initialization vector is a properly base64 encoded string
-  // for both payloads.
-  std::string function_iv_string =
-      base::SysNSStringToUTF8(params.encoded_function_iv);
-  std::string decoded_function_iv;
-  EXPECT_TRUE(base::Base64Decode(function_iv_string, &decoded_function_iv));
-  std::string message_iv_string =
-      base::SysNSStringToUTF8(params.encoded_message_iv);
-  std::string decoded_message_iv;
-  EXPECT_TRUE(base::Base64Decode(message_iv_string, &decoded_message_iv));
-
-  // Ensure the frame ID matches.
-  EXPECT_NSEQ(base::SysUTF8ToNSString(kFrameId), params.frame_id);
-}
-
-// Tests that the WebFrame uses different initialization vectors for two
-// sequential calls to |CallJavaScriptFunction|.
-TEST_F(WebFrameImplTest, CallJavaScriptFunctionUniqueInitializationVector) {
-  FakeWebState fake_web_state;
-  GURL security_origin;
-  WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
-                         /*is_main_frame=*/false, security_origin,
-                         &fake_web_state);
-  web_frame.SetEncryptionKey(CreateKey());
-
-  std::vector<base::Value> function_params;
-  function_params.push_back(base::Value("plaintextParam"));
-  EXPECT_TRUE(
-      web_frame.CallJavaScriptFunction("functionName", function_params));
-
-  NSString* last_script1 =
-      base::SysUTF16ToNSString(fake_web_state.GetLastExecutedJavascript());
-  RouteMessageParameters params1 =
-      ParametersFromFunctionCallString(last_script1);
-
-  // Call JavaScript Function again to verify that the same initialization
-  // vector is not reused and that the ciphertext is different.
-  EXPECT_TRUE(
-      web_frame.CallJavaScriptFunction("functionName", function_params));
-  NSString* last_script2 =
-      base::SysUTF16ToNSString(fake_web_state.GetLastExecutedJavascript());
-  RouteMessageParameters params2 =
-      ParametersFromFunctionCallString(last_script2);
-
-  EXPECT_NSNE(params1.encoded_function_payload,
-              params2.encoded_function_payload);
-  EXPECT_NSNE(params1.encoded_function_iv, params2.encoded_function_iv);
-}
-
-// Tests that the WebFrame properly encodes and encrypts all parameters for
-// |CallJavaScriptFunction|.
-TEST_F(WebFrameImplTest, CallJavaScriptFunctionMessageProperlyEncoded) {
-  std::unique_ptr<SymmetricKey> key = CreateKey();
-  const std::string key_string = key->key();
-  // Use an arbitrary nonzero message id to ensure it isn't matching a zero
-  // value by chance.
-  const int initial_message_id = 11;
-
-  FakeWebState fake_web_state;
-  GURL security_origin;
-  WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
-                         /*is_main_frame=*/false, security_origin,
-                         &fake_web_state);
-  web_frame.SetEncryptionKey(std::move(key));
-  web_frame.SetNextMessageId(initial_message_id);
-
-  std::vector<base::Value> function_params;
-  std::string plaintext_param("plaintextParam");
-  function_params.push_back(base::Value(plaintext_param));
-  EXPECT_TRUE(
-      web_frame.CallJavaScriptFunction("functionName", function_params));
-
-  NSString* last_script =
-      base::SysUTF16ToNSString(fake_web_state.GetLastExecutedJavascript());
-  RouteMessageParameters params = ParametersFromFunctionCallString(last_script);
-
-  std::string decoded_function_ciphertext;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_function_payload),
-      &decoded_function_ciphertext));
-
-  std::string decoded_function_iv;
-  EXPECT_TRUE(
-      base::Base64Decode(base::SysNSStringToUTF8(params.encoded_function_iv),
-                         &decoded_function_iv));
-
-  std::string decoded_message_ciphertext;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_message_payload),
-      &decoded_message_ciphertext));
-
-  std::string decoded_message_iv;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_message_iv), &decoded_message_iv));
-
-  // Decrypt message
-  crypto::Aead aead(crypto::Aead::AES_256_GCM);
-  aead.Init(&key_string);
-  std::string function_plaintext;
-  EXPECT_TRUE(aead.Open(decoded_function_ciphertext, decoded_function_iv,
-                        base::NumberToString(initial_message_id),
-                        &function_plaintext));
-  std::string message_plaintext;
-  EXPECT_TRUE(aead.Open(decoded_message_ciphertext, decoded_message_iv,
-                        /*additional_data=*/"", &message_plaintext));
-
-  absl::optional<base::Value> parsed_function_result =
-      base::JSONReader::Read(function_plaintext, false);
-  EXPECT_TRUE(parsed_function_result.has_value());
-  ASSERT_TRUE(parsed_function_result.value().is_dict());
-
-  const std::string* decrypted_function_name =
-      parsed_function_result.value().FindStringKey("functionName");
-  ASSERT_TRUE(decrypted_function_name);
-  EXPECT_EQ("functionName", *decrypted_function_name);
-
-  base::Value* decrypted_parameters =
-      parsed_function_result.value().FindKeyOfType("parameters",
-                                                   base::Value::Type::LIST);
-  ASSERT_TRUE(decrypted_parameters);
-  ASSERT_EQ(function_params.size(),
-            decrypted_parameters->GetListDeprecated().size());
-  EXPECT_EQ(plaintext_param,
-            decrypted_parameters->GetListDeprecated()[0].GetString());
-
-  absl::optional<base::Value> parsed_message_result =
-      base::JSONReader::Read(message_plaintext, false);
-  EXPECT_TRUE(parsed_message_result.has_value());
-  ASSERT_TRUE(parsed_message_result.value().is_dict());
-
-  absl::optional<int> decrypted_message_id =
-      parsed_message_result.value().FindIntKey("messageId");
-  ASSERT_TRUE(decrypted_message_id.has_value());
-  EXPECT_EQ(decrypted_message_id.value(), initial_message_id);
-
-  absl::optional<bool> decrypted_respond_with_result =
-      parsed_message_result.value().FindBoolKey("replyWithResult");
-  ASSERT_TRUE(decrypted_respond_with_result.has_value());
-  EXPECT_FALSE(decrypted_respond_with_result.value());
-}
-
-// Tests that the WebFrame properly encodes and encrypts the respondWithResult
-// value when |CallJavaScriptFunction| is called with a callback.
-TEST_F(WebFrameImplTest, CallJavaScriptFunctionRespondWithResult) {
-  std::unique_ptr<SymmetricKey> key = CreateKey();
-  const std::string key_string = key->key();
-  // Use an arbitrary nonzero message id to ensure it isn't matching a zero
-  // value by chance.
-  const int initial_message_id = 11;
-
-  FakeWebState fake_web_state;
-  GURL security_origin;
-  WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
-                         /*is_main_frame=*/false, security_origin,
-                         &fake_web_state);
-  web_frame.SetEncryptionKey(std::move(key));
-  web_frame.SetNextMessageId(initial_message_id);
-
-  std::vector<base::Value> function_params;
-  std::string plaintext_param("plaintextParam");
-  function_params.push_back(base::Value(plaintext_param));
-  EXPECT_TRUE(web_frame.CallJavaScriptFunction(
-      "functionName", function_params,
-      base::BindOnce(^(const base::Value* value){
-      }),
-      base::Seconds(5)));
-
-  NSString* last_script =
-      base::SysUTF16ToNSString(fake_web_state.GetLastExecutedJavascript());
-  RouteMessageParameters params = ParametersFromFunctionCallString(last_script);
-
-  std::string decoded_message_ciphertext;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_message_payload),
-      &decoded_message_ciphertext));
-
-  std::string decoded_message_iv;
-  EXPECT_TRUE(base::Base64Decode(
-      base::SysNSStringToUTF8(params.encoded_message_iv), &decoded_message_iv));
-
-  // Decrypt message
-  crypto::Aead aead(crypto::Aead::AES_256_GCM);
-  aead.Init(&key_string);
-  std::string message_plaintext;
-  EXPECT_TRUE(aead.Open(decoded_message_ciphertext, decoded_message_iv,
-                        /*additional_data=*/"", &message_plaintext));
-
-  absl::optional<base::Value> parsed_result =
-      base::JSONReader::Read(message_plaintext, false);
-  EXPECT_TRUE(parsed_result.has_value());
-  ASSERT_TRUE(parsed_result.value().is_dict());
-
-  absl::optional<bool> decrypted_respond_with_result =
-      parsed_result.value().FindBoolKey("replyWithResult");
-  ASSERT_TRUE(decrypted_respond_with_result.has_value());
-  EXPECT_TRUE(decrypted_respond_with_result.value());
-}
-
 // Tests that the WebFrame properly creates JavaScript for the main frame when
 // there is no encryption key.
 TEST_F(WebFrameImplTest, CallJavaScriptFunctionMainFrameWithoutKey) {
   FakeWebState fake_web_state;
+  fake_web_state.SetBrowserState(GetBrowserState());
   GURL security_origin;
   WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
                          /*is_main_frame=*/true, security_origin,
@@ -434,6 +134,7 @@
 // is no encryption key.
 TEST_F(WebFrameImplTest, CallJavaScriptFunctionIFrameFrameWithoutKey) {
   FakeWebState fake_web_state;
+  fake_web_state.SetBrowserState(GetBrowserState());
   GURL security_origin;
   WebFrameImpl web_frame([[WKFrameInfo alloc] init], kFrameId,
                          /*is_main_frame=*/false, security_origin,
@@ -453,6 +154,7 @@
 // if and only if it is a main frame.
 TEST_F(WebFrameImplTest, ExecuteJavaScript) {
   FakeWebState fake_web_state;
+  fake_web_state.SetBrowserState(GetBrowserState());
   GURL security_origin;
 
   NSString* script = @"__gCrWeb = {};"
@@ -477,6 +179,8 @@
 // a callback if and only if it is a main frame.
 TEST_F(WebFrameImplTest, ExecuteJavaScriptWithCallback) {
   FakeWebState fake_web_state;
+  fake_web_state.SetBrowserState(GetBrowserState());
+
   GURL security_origin;
 
   NSString* script = @"__gCrWeb = {};"
diff --git a/ios/web/public/test/web_view_interaction_test_util.mm b/ios/web/public/test/web_view_interaction_test_util.mm
index aaf45289..39e2571 100644
--- a/ios/web/public/test/web_view_interaction_test_util.mm
+++ b/ios/web/public/test/web_view_interaction_test_util.mm
@@ -102,6 +102,9 @@
                   << "JavaScriptFeature does not appear to be configured.";
       return nullptr;
     }
+  } else {
+    world = JavaScriptFeatureManager::GetPageContentWorldForBrowserState(
+        web_state->GetBrowserState());
   }
 
   WebFrameImpl* frame = static_cast<WebFrameImpl*>(GetMainFrame(web_state));
diff --git a/ios/web/public/webui/web_ui_ios.h b/ios/web/public/webui/web_ui_ios.h
index 5e676bc..130e7af 100644
--- a/ios/web/public/webui/web_ui_ios.h
+++ b/ios/web/public/webui/web_ui_ios.h
@@ -49,17 +49,6 @@
   virtual void RegisterMessageCallback(const std::string& message,
                                        MessageCallback callback) = 0;
 
-  // TODO(crbug.com/1243386): Instances of RegisterDeprecatedMessageCallback()
-  // should be migrated to RegisterMessageCallback().
-  //
-  // Used by WebUIIOSMessageHandlers. If the given message is already
-  // registered, the call has no effect.
-  using DeprecatedMessageCallback =
-      base::RepeatingCallback<void(const base::ListValue*)>;
-  virtual void RegisterDeprecatedMessageCallback(
-      const std::string& message,
-      const DeprecatedMessageCallback& callback) = 0;
-
   // This is only needed if an embedder overrides handling of a WebUIIOSMessage
   // and then later wants to undo that, or to route it to a different WebUIIOS
   // object.
diff --git a/ios/web/webui/web_ui_ios_impl.h b/ios/web/webui/web_ui_ios_impl.h
index 447ac60..184990cd 100644
--- a/ios/web/webui/web_ui_ios_impl.h
+++ b/ios/web/webui/web_ui_ios_impl.h
@@ -39,9 +39,6 @@
       std::unique_ptr<WebUIIOSMessageHandler> handler) override;
   void RegisterMessageCallback(const std::string& message,
                                MessageCallback callback) override;
-  void RegisterDeprecatedMessageCallback(
-      const std::string& message,
-      const DeprecatedMessageCallback& callback) override;
   void ProcessWebUIIOSMessage(const GURL& source_url,
                               const std::string& message,
                               const base::Value& args) override;
@@ -68,13 +65,6 @@
   using MessageCallbackMap = std::map<std::string, MessageCallback>;
   MessageCallbackMap message_callbacks_;
 
-  // A map of message name -> message handling callback.
-  // TODO(crbug.com/1243386): Remove once RegisterDeprecatedMessageCallback()
-  // instances are migrated to RegisterMessageCallback().
-  using DeprecatedMessageCallbackMap =
-      std::map<std::string, DeprecatedMessageCallback>;
-  DeprecatedMessageCallbackMap deprecated_message_callbacks_;
-
   // The WebUIIOSMessageHandlers we own.
   std::vector<std::unique_ptr<WebUIIOSMessageHandler>> handlers_;
 
diff --git a/ios/web/webui/web_ui_ios_impl.mm b/ios/web/webui/web_ui_ios_impl.mm
index d72ea439..68120d66 100644
--- a/ios/web/webui/web_ui_ios_impl.mm
+++ b/ios/web/webui/web_ui_ios_impl.mm
@@ -114,12 +114,6 @@
   message_callbacks_.emplace(message, std::move(callback));
 }
 
-void WebUIIOSImpl::RegisterDeprecatedMessageCallback(
-    const std::string& message,
-    const DeprecatedMessageCallback& callback) {
-  deprecated_message_callbacks_.emplace(message, callback);
-}
-
 void WebUIIOSImpl::OnJsMessage(const base::Value& message,
                                const GURL& page_url,
                                bool user_is_interacting,
@@ -160,13 +154,6 @@
     message_callback_it->second.Run(args.GetList());
     return;
   }
-
-  // Look up the deprecated callback for this message.
-  auto deprecated_callback_it = deprecated_message_callbacks_.find(message);
-  if (deprecated_callback_it != deprecated_message_callbacks_.end()) {
-    // Forward this message and content on.
-    deprecated_callback_it->second.Run(&base::Value::AsListValue(args));
-  }
 }
 
 // WebUIIOSImpl, protected:
diff --git a/ipc/ipc_message_protobuf_utils.h b/ipc/ipc_message_protobuf_utils.h
index ad3638cd..f06a176 100644
--- a/ipc/ipc_message_protobuf_utils.h
+++ b/ipc/ipc_message_protobuf_utils.h
@@ -25,14 +25,15 @@
   static bool Read(const base::Pickle* m,
                    base::PickleIterator* iter,
                    param_type* r) {
-    size_t size;
+    int size;
+    // ReadLength() checks for < 0 itself.
     if (!iter->ReadLength(&size))
       return false;
     // Avoid integer overflow / assertion failure in Reserve() function.
-    if (size > INT_MAX / sizeof(StorageType))
+    if (INT_MAX / sizeof(StorageType) <= static_cast<size_t>(size))
       return false;
     r->Reserve(size);
-    for (size_t i = 0; i < size; i++) {
+    for (int i = 0; i < size; i++) {
       if (!ReadParam(m, iter, r->Add()))
         return false;
     }
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index 04bdab4d..4fc11f0 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -135,7 +135,7 @@
     }
     case base::Value::Type::BINARY: {
       pickle->WriteData(reinterpret_cast<const char*>(value.GetBlob().data()),
-                        value.GetBlob().size());
+                        base::checked_cast<int>(value.GetBlob().size()));
       break;
     }
     case base::Value::Type::DICT: {
@@ -433,7 +433,7 @@
   if (p.empty()) {
     m->WriteData(NULL, 0);
   } else {
-    m->WriteData(&p.front(), p.size());
+    m->WriteData(&p.front(), base::checked_cast<int>(p.size()));
   }
 }
 
@@ -441,8 +441,8 @@
                                           base::PickleIterator* iter,
                                           param_type* r) {
   const char *data;
-  size_t data_size = 0;
-  if (!iter->ReadData(&data, &data_size))
+  int data_size = 0;
+  if (!iter->ReadData(&data, &data_size) || data_size < 0)
     return false;
   r->resize(data_size);
   if (data_size)
@@ -459,7 +459,8 @@
   if (p.empty()) {
     m->WriteData(NULL, 0);
   } else {
-    m->WriteData(reinterpret_cast<const char*>(&p.front()), p.size());
+    m->WriteData(reinterpret_cast<const char*>(&p.front()),
+                 base::checked_cast<int>(p.size()));
   }
 }
 
@@ -467,8 +468,8 @@
                                                    base::PickleIterator* iter,
                                                    param_type* r) {
   const char *data;
-  size_t data_size = 0;
-  if (!iter->ReadData(&data, &data_size))
+  int data_size = 0;
+  if (!iter->ReadData(&data, &data_size) || data_size < 0)
     return false;
   r->resize(data_size);
   if (data_size)
@@ -494,11 +495,12 @@
 bool ParamTraits<std::vector<bool>>::Read(const base::Pickle* m,
                                           base::PickleIterator* iter,
                                           param_type* r) {
-  size_t size;
+  int size;
+  // ReadLength() checks for < 0 itself.
   if (!iter->ReadLength(&size))
     return false;
   r->resize(size);
-  for (size_t i = 0; i < size; i++) {
+  for (int i = 0; i < size; i++) {
     bool value;
     if (!ReadParam(m, iter, &value))
       return false;
@@ -1435,7 +1437,7 @@
   m->WriteUInt32(static_cast<uint32_t>(p.routing_id()));
   m->WriteUInt32(p.type());
   m->WriteUInt32(p.flags());
-  m->WriteData(p.payload(), p.payload_size());
+  m->WriteData(p.payload(), static_cast<uint32_t>(p.payload_size()));
 }
 
 bool ParamTraits<Message>::Read(const base::Pickle* m,
@@ -1447,7 +1449,7 @@
       !iter->ReadUInt32(&flags))
     return false;
 
-  size_t payload_size;
+  int payload_size;
   const char* payload;
   if (!iter->ReadData(&payload, &payload_size))
     return false;
@@ -1490,7 +1492,7 @@
                             base::PickleIterator* iter,
                             param_type* r) {
   const char *data;
-  size_t data_size = 0;
+  int data_size = 0;
   bool result = iter->ReadData(&data, &data_size);
   if (result && data_size == sizeof(MSG)) {
     memcpy(r, data, sizeof(MSG));
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 3462b855..0d9d5c2 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -404,15 +404,15 @@
   static bool Read(const base::Pickle* m,
                    base::PickleIterator* iter,
                    param_type* r) {
-    size_t size;
+    int size;
     // ReadLength() checks for < 0 itself.
     if (!iter->ReadLength(&size))
       return false;
     // Resizing beforehand is not safe, see BUG 1006367 for details.
-    if (size > INT_MAX / sizeof(P))
+    if (INT_MAX / sizeof(P) <= static_cast<size_t>(size))
       return false;
     r->resize(size);
-    for (size_t i = 0; i < size; i++) {
+    for (int i = 0; i < size; i++) {
       if (!ReadParam(m, iter, &(*r)[i]))
         return false;
     }
@@ -439,10 +439,10 @@
   static bool Read(const base::Pickle* m,
                    base::PickleIterator* iter,
                    param_type* r) {
-    size_t size;
+    int size;
     if (!iter->ReadLength(&size))
       return false;
-    for (size_t i = 0; i < size; ++i) {
+    for (int i = 0; i < size; ++i) {
       P item;
       if (!ReadParam(m, iter, &item))
         return false;
@@ -884,14 +884,15 @@
   static bool Read(const base::Pickle* m,
                    base::PickleIterator* iter,
                    param_type* r) {
-    size_t size;
+    int size;
+    // ReadLength() checks for < 0 itself.
     if (!iter->ReadLength(&size))
       return false;
     // Sanity check for the vector size.
-    if (size > INT_MAX / sizeof(P))
+    if (INT_MAX / sizeof(P) <= static_cast<size_t>(size))
       return false;
     P value;
-    for (size_t i = 0; i < size; i++) {
+    for (int i = 0; i < size; i++) {
       if (!ReadParam(m, iter, &value))
         return false;
       (*r)->push_back(value);
@@ -921,7 +922,7 @@
   static bool Read(const base::Pickle* m,
                    base::PickleIterator* iter,
                    param_type* r) {
-    size_t size;
+    int size;
     if (!iter->ReadLength(&size))
       return false;
 
@@ -930,7 +931,7 @@
     // serialized ones will still be handled properly.
     std::vector<typename param_type::value_type> vect;
     vect.resize(size);
-    for (size_t i = 0; i < size; ++i) {
+    for (int i = 0; i < size; ++i) {
       if (!ReadParam(m, iter, &vect[i].first))
         return false;
       if (!ReadParam(m, iter, &vect[i].second))
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 9be78fdd..c820538 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -211,18 +211,16 @@
       "//media/fuchsia/video:unittests",
     ]
 
-    use_cfv1 = false
-
-    # TODO(https://crbug.com/1185811): Investigate removing the requirement for
-    # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests.
-    # PaintCanvasVideoRendererWithGLTest.CopyVideoFrameYUVDataToGLTexture
-    # crashes without it.
-    test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
-
+    use_cfv2 = false
     additional_manifest_fragments = [
-      "//build/config/fuchsia/test/audio_output.shard.test-cml",
-      "//build/config/fuchsia/test/platform_video_codecs.shard.test-cml",
-      "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
+      "//build/config/fuchsia/test/audio_capabilities.test-cmx",
+
+      # TODO(crbug.com/1185811): Figure out why
+      # PaintCanvasVideoRendererWithGLTest.CopyVideoFrameYUVDataToGLTexture
+      # crashes without "deprecated-ambient-replace-as-executable".
+      "//build/config/fuchsia/test/jit_capabilities.test-cmx",
+
+      "//build/config/fuchsia/test/vulkan_capabilities.test-cmx",
     ]
   }
 
@@ -283,9 +281,9 @@
   }
 
   if (is_fuchsia) {
-    use_cfv1 = false
+    use_cfv2 = false
     additional_manifest_fragments =
-        [ "//build/config/fuchsia/test/audio_output.shard.test-cml" ]
+        [ "//build/config/fuchsia/test/audio_capabilities.test-cmx" ]
   }
 }
 
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index a7dedda..8b90335 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -471,8 +471,8 @@
 
   scoped_refptr<MmapedBuffer> buffer = queue->GetBuffer(0);
 
-  memcpy(static_cast<uint8_t*>(buffer->mmaped_planes()[0].start_addr),
-         ivf_frame_data_, ivf_frame_header_.frame_size);
+  buffer->mmaped_planes()[0].CopyIn(ivf_frame_data_,
+                                    ivf_frame_header_.frame_size);
 }
 
 std::set<int> Av1Decoder::RefreshReferenceSlots(
diff --git a/media/gpu/v4l2/test/v4l2_ioctl_shim.cc b/media/gpu/v4l2/test/v4l2_ioctl_shim.cc
index f864c7c..555dbe6 100644
--- a/media/gpu/v4l2/test/v4l2_ioctl_shim.cc
+++ b/media/gpu/v4l2/test/v4l2_ioctl_shim.cc
@@ -114,7 +114,7 @@
 }
 
 MmapedBuffer::~MmapedBuffer() {
-  for (const auto& [start_addr, length] : mmaped_planes_)
+  for (const auto& [start_addr, length, bytes_used] : mmaped_planes_)
     munmap(start_addr, length);
 }
 
@@ -400,7 +400,7 @@
 
   for (uint32_t i = 0; i < queue->num_planes(); ++i) {
     v4l2_buffer.m.planes[i].length = buffer->mmaped_planes()[i].length;
-    v4l2_buffer.m.planes[i].bytesused = buffer->mmaped_planes()[i].length;
+    v4l2_buffer.m.planes[i].bytesused = buffer->mmaped_planes()[i].bytes_used;
     v4l2_buffer.m.planes[i].data_offset = 0;
   }
 
diff --git a/media/gpu/v4l2/test/v4l2_ioctl_shim.h b/media/gpu/v4l2/test/v4l2_ioctl_shim.h
index d2ebe46..d9b83cb 100644
--- a/media/gpu/v4l2/test/v4l2_ioctl_shim.h
+++ b/media/gpu/v4l2/test/v4l2_ioctl_shim.h
@@ -6,8 +6,10 @@
 #define MEDIA_GPU_V4L2_TEST_V4L2_IOCTL_SHIM_H_
 
 #include <linux/videodev2.h>
+#include <string.h>
 
 #include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -25,17 +27,22 @@
   class MmapedPlane {
    public:
     void* start_addr;
-    size_t length;
+    const size_t length;
+    size_t bytes_used;
 
-    MmapedPlane(void* start, size_t len) {
-      start_addr = start;
-      length = len;
+    MmapedPlane(void* start, size_t len) : start_addr(start), length(len){};
+
+    void CopyIn(const uint8_t* frame_data, size_t frame_size) {
+      LOG_ASSERT(frame_size < length)
+          << "Not enough memory allocated to copy into.";
+      bytes_used = frame_size;
+      memcpy(static_cast<uint8_t*>(start_addr), frame_data, frame_size);
     }
   };
 
   using MmapedPlanes = std::vector<MmapedPlane>;
 
-  MmapedPlanes mmaped_planes() const { return mmaped_planes_; }
+  MmapedPlanes& mmaped_planes() { return mmaped_planes_; }
 
   uint32_t buffer_id() const { return buffer_id_; }
   void set_buffer_id(uint32_t buffer_id) { buffer_id_ = buffer_id; }
diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc
index b99b323..337d906e 100644
--- a/media/gpu/v4l2/test/vp9_decoder.cc
+++ b/media/gpu/v4l2/test/vp9_decoder.cc
@@ -416,8 +416,7 @@
 
   scoped_refptr<MmapedBuffer> buffer = queue->GetBuffer(0);
 
-  memcpy(static_cast<uint8_t*>(buffer->mmaped_planes()[0].start_addr),
-         frame_hdr.data, frame_hdr.frame_size);
+  buffer->mmaped_planes()[0].CopyIn(frame_hdr.data, frame_hdr.frame_size);
 }
 
 VideoDecoder::Result Vp9Decoder::DecodeNextFrame(std::vector<char>& y_plane,
diff --git a/net/cert/x509_certificate.cc b/net/cert/x509_certificate.cc
index aef15a6..17d93695 100644
--- a/net/cert/x509_certificate.cc
+++ b/net/cert/x509_certificate.cc
@@ -205,14 +205,14 @@
 scoped_refptr<X509Certificate> X509Certificate::CreateFromPickleUnsafeOptions(
     base::PickleIterator* pickle_iter,
     UnsafeCreateOptions options) {
-  size_t chain_length = 0;
+  int chain_length = 0;
   if (!pickle_iter->ReadLength(&chain_length))
     return nullptr;
 
   std::vector<base::StringPiece> cert_chain;
   const char* data = nullptr;
-  size_t data_length = 0;
-  for (size_t i = 0; i < chain_length; ++i) {
+  int data_length = 0;
+  for (int i = 0; i < chain_length; ++i) {
     if (!pickle_iter->ReadData(&data, &data_length))
       return nullptr;
     cert_chain.push_back(base::StringPiece(data, data_length));
diff --git a/net/http/OWNERS b/net/http/OWNERS
index c01a1d5..49c38c9 100644
--- a/net/http/OWNERS
+++ b/net/http/OWNERS
@@ -2,4 +2,6 @@
 per-file transport_security_state_static.*=estark@chromium.org
 per-file transport_security_state_static.*=cthomp@chromium.org
 per-file transport_security_state_static.*=jdeblasio@chromium.org
+# For automated updates
+per-file transport_security_state_static.*=mdb.chrome-pki-metadata@google.com
 per-file *test*=file://net/quic/OWNERS # for QUIC refactors
diff --git a/ppapi/proxy/ppapi_param_traits.cc b/ppapi/proxy/ppapi_param_traits.cc
index 1e7cab1..f7c06695 100644
--- a/ppapi/proxy/ppapi_param_traits.cc
+++ b/ppapi/proxy/ppapi_param_traits.cc
@@ -43,15 +43,16 @@
                            base::PickleIterator* iter,
                            std::vector<T>* output) {
   // This part is just a copy of the the default ParamTraits vector Read().
-  size_t size;
+  int size;
+  // ReadLength() checks for < 0 itself.
   if (!iter->ReadLength(&size))
     return false;
   // Resizing beforehand is not safe, see BUG 1006367 for details.
-  if (size > INT_MAX / sizeof(T))
+  if (INT_MAX / sizeof(T) <= static_cast<size_t>(size))
     return false;
 
   output->reserve(size);
-  for (size_t i = 0; i < size; i++) {
+  for (int i = 0; i < size; i++) {
     T cur;
     if (!ReadParam(m, iter, &cur))
       return false;
@@ -104,7 +105,7 @@
 void ParamTraits<PP_NetAddress_Private>::Write(base::Pickle* m,
                                                const param_type& p) {
   WriteParam(m, p.size);
-  m->WriteBytes(p.data, p.size);
+  m->WriteBytes(p.data, static_cast<int>(p.size));
 }
 
 // static
diff --git a/ppapi/proxy/url_loader_resource.cc b/ppapi/proxy/url_loader_resource.cc
index d1808055..af942ea 100644
--- a/ppapi/proxy/url_loader_resource.cc
+++ b/ppapi/proxy/url_loader_resource.cc
@@ -238,7 +238,7 @@
     const IPC::Message& message) {
   base::PickleIterator iter(message);
   const char* data;
-  size_t data_length;
+  int data_length;
   if (!iter.ReadData(&data, &data_length)) {
     NOTREACHED() << "Expecting data";
     return;
diff --git a/services/network/public/cpp/net_ipc_param_traits.cc b/services/network/public/cpp/net_ipc_param_traits.cc
index c6716cb..a8cf66d6 100644
--- a/services/network/public/cpp/net_ipc_param_traits.cc
+++ b/services/network/public/cpp/net_ipc_param_traits.cc
@@ -163,10 +163,10 @@
                                                 base::PickleIterator* iter,
                                                 param_type* r) {
   // Sanity check.
-  size_t size;
+  int size;
   if (!iter->ReadLength(&size))
     return false;
-  for (size_t i = 0; i < size; ++i) {
+  for (int i = 0; i < size; ++i) {
     net::HttpRequestHeaders::HeaderKeyValuePair pair;
     if (!ReadParam(m, iter, &pair) ||
         !net::HttpUtil::IsValidHeaderName(pair.key) ||
diff --git a/skia/ext/skia_utils_base.cc b/skia/ext/skia_utils_base.cc
index baa30d1..96fc35e 100644
--- a/skia/ext/skia_utils_base.cc
+++ b/skia/ext/skia_utils_base.cc
@@ -18,7 +18,7 @@
 namespace skia {
 
 bool ReadSkString(base::PickleIterator* iter, SkString* str) {
-  size_t reply_length;
+  int reply_length;
   const char* reply_text;
 
   if (!iter->ReadData(&reply_text, &reply_length))
@@ -33,7 +33,7 @@
                         SkFontConfigInterface::FontIdentity* identity) {
   uint32_t reply_id;
   uint32_t reply_ttcIndex;
-  size_t reply_length;
+  int reply_length;
   const char* reply_text;
 
   if (!iter->ReadUInt32(&reply_id) ||
diff --git a/storage/browser/file_system/file_system_usage_cache.cc b/storage/browser/file_system/file_system_usage_cache.cc
index 515b1a2..5b16535 100644
--- a/storage/browser/file_system/file_system_usage_cache.cc
+++ b/storage/browser/file_system/file_system_usage_cache.cc
@@ -36,7 +36,7 @@
 const base::FilePath::CharType FileSystemUsageCache::kUsageFileName[] =
     FILE_PATH_LITERAL(".usage");
 const char FileSystemUsageCache::kUsageFileHeader[] = "FSU5";
-const size_t FileSystemUsageCache::kUsageFileHeaderSize = 4;
+const int FileSystemUsageCache::kUsageFileHeaderSize = 4;
 
 // Pickle::{Read,Write}Bool treat bool as int
 const int FileSystemUsageCache::kUsageFileSize =
diff --git a/storage/browser/file_system/file_system_usage_cache.h b/storage/browser/file_system/file_system_usage_cache.h
index 75bdd0f..ef0486778 100644
--- a/storage/browser/file_system/file_system_usage_cache.h
+++ b/storage/browser/file_system/file_system_usage_cache.h
@@ -61,7 +61,7 @@
   static const base::FilePath::CharType kUsageFileName[];
   static const char kUsageFileHeader[];
   static const int kUsageFileSize;
-  static const size_t kUsageFileHeaderSize;
+  static const int kUsageFileHeaderSize;
 
  private:
   // Read the size, validity and the "dirty" entry described in the .usage file.
diff --git a/testing/buildbot/OWNERS b/testing/buildbot/OWNERS
index e470d6f..3f5012de 100644
--- a/testing/buildbot/OWNERS
+++ b/testing/buildbot/OWNERS
@@ -2,7 +2,7 @@
 # run tests and understand the implications of changing these files.
 
 # If you're making changes that affect CQ builders (like adding new tests or
-# compile targets) please get a review from someone in //infra/OWNERS.
+# compile targets) you need a review from a Trooper in //infra/OWNERS.
 file://infra/OWNERS
 
 jam@chromium.org
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 64d3a97..2f0dae19 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -9080,6 +9080,28 @@
       }
     ]
   },
+  "Win x64 Builder reclient gVisor cross test": {
+    "additional_compile_targets": [
+      "pdf_fuzzers"
+    ],
+    "scripts": [
+      {
+        "name": "check_network_annotations",
+        "script": "check_network_annotations.py",
+        "swarming": {}
+      },
+      {
+        "name": "metrics_python_tests",
+        "script": "metrics_python_tests.py",
+        "swarming": {}
+      },
+      {
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
+      }
+    ]
+  },
   "Win x64 Builder reclient staging": {
     "additional_compile_targets": [
       "pdf_fuzzers"
diff --git a/testing/buildbot/filters/chromeos.amd64-generic.gl_tests_validating.filter b/testing/buildbot/filters/chromeos.amd64-generic.gl_tests_validating.filter
index 1dbc318..8e7c3a2b 100644
--- a/testing/buildbot/filters/chromeos.amd64-generic.gl_tests_validating.filter
+++ b/testing/buildbot/filters/chromeos.amd64-generic.gl_tests_validating.filter
@@ -5,22 +5,8 @@
 
 # crbug.com/1314115
 -SharedImageGLBackingProduceDawnTest.Basic
--WebGPUMailboxTest.AssociateMailboxCmd*
--WebGPUMailboxTest.AssociateOnTwoDevicesAtTheSameTime*
--WebGPUMailboxTest.DissociateMailboxCmd*
--WebGPUMailboxTest.ErrorWhenUsingTextureAfterDissociate*
--WebGPUMailboxTest.ReadUninitializedSharedImage*
--WebGPUMailboxTest.ReadWritableUninitializedSharedImage*
--WebGPUMailboxTest.UseA_UseB_DestroyA_DestroyB*
--WebGPUMailboxTest.WriteToMailboxThenReadFromIt*
--WebGPUTest.RequestDeviceAfterContextLost
--WebGPUTest.FlushNoCommands
--WebGPUTest.ImplicitFallbackAdapterIsDisallowed
--WebGPUTest.ReportLoss
--WebGPUTest.ReportLossReentrant
--WebGPUTest.RequestAdapterAfterContextLost
--WebGPUTest.RequestDeviceWitUnsupportedFeature
--WebGPUTest.SPIRVIsDisallowed
+-WebGPUMailboxTest.*
+-WebGPUTest.*
 
 # crbug.com/1314118
 -TranslatorVariants/EXTBlendFuncExtendedDrawTest.ESSL1FragColor*
diff --git a/testing/buildbot/filters/chromeos.kevin.gl_tests_validating.filter b/testing/buildbot/filters/chromeos.kevin.gl_tests_validating.filter
index 96b26550..a848f717 100644
--- a/testing/buildbot/filters/chromeos.kevin.gl_tests_validating.filter
+++ b/testing/buildbot/filters/chromeos.kevin.gl_tests_validating.filter
@@ -5,22 +5,8 @@
 
 # crbug.com/1314115
 -SharedImageGLBackingProduceDawnTest.Basic
--WebGPUMailboxTest.AssociateMailboxCmd*
--WebGPUMailboxTest.AssociateOnTwoDevicesAtTheSameTime*
--WebGPUMailboxTest.DissociateMailboxCmd*
--WebGPUMailboxTest.ErrorWhenUsingTextureAfterDissociate*
--WebGPUMailboxTest.ReadUninitializedSharedImage*
--WebGPUMailboxTest.ReadWritableUninitializedSharedImage*
--WebGPUMailboxTest.UseA_UseB_DestroyA_DestroyB*
--WebGPUMailboxTest.WriteToMailboxThenReadFromIt*
--WebGPUTest.RequestDeviceAfterContextLost
--WebGPUTest.FlushNoCommands
--WebGPUTest.ImplicitFallbackAdapterIsDisallowed
--WebGPUTest.ReportLoss
--WebGPUTest.ReportLossReentrant
--WebGPUTest.RequestAdapterAfterContextLost
--WebGPUTest.RequestDeviceWitUnsupportedFeature
--WebGPUTest.SPIRVIsDisallowed
+-WebGPUMailboxTest.*
+-WebGPUTest.*
 
 # crbug.com/1314124
 -ExternalVkImageFactoryTest.SkiaVulkanWrite_DawnRead
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 375641b..54d5a11 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2944,6 +2944,16 @@
           'scripts': 'chromium_win_scripts',
         },
       },
+      'Win x64 Builder reclient gVisor cross test': {
+        # Copied from
+        # https://source.chromium.org/chromium/chromium/src/+/7b147a6777cb32d6a12e1716c61a0ed50dc1229a:testing/buildbot/waterfalls.pyl;l=6023-6030
+        'additional_compile_targets': [
+          'pdf_fuzzers'
+        ],
+        'test_suites': {
+          'scripts': 'chromium_win_scripts',
+        },
+      },
       'Win x64 Builder reclient staging': {
         # Copied from
         # https://source.chromium.org/chromium/chromium/src/+/7b147a6777cb32d6a12e1716c61a0ed50dc1229a:testing/buildbot/waterfalls.pyl;l=6023-6030
diff --git a/testing/merge_scripts/OWNERS b/testing/merge_scripts/OWNERS
index 1d77d829..d591a3e 100644
--- a/testing/merge_scripts/OWNERS
+++ b/testing/merge_scripts/OWNERS
@@ -1,2 +1 @@
 kbr@chromium.org
-martiniss@chromium.org
diff --git a/testing/scripts/OWNERS b/testing/scripts/OWNERS
index ee216de..c784a37 100644
--- a/testing/scripts/OWNERS
+++ b/testing/scripts/OWNERS
@@ -3,7 +3,6 @@
 
 bsheedy@chromium.org
 kbr@chromium.org
-martiniss@chromium.org
 
 per-file check_static_initializers.py=thakis@chromium.org
 per-file check_static_initializers.py=thomasanderson@chromium.org
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 53300f9c..1e26506 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -814,27 +814,6 @@
             ]
         }
     ],
-    "AutofillDelayPopupControllerDeletion": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillDelayPopupControllerDeletion"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillEnableAugmentedPhoneCountryCode": [
         {
             "platforms": [
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index d398bccf..8162614 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -66,15 +66,9 @@
     deps = [ "$google_play_services_package:google_play_services_base_java" ]
   }
 
-  android_library("chromium_play_services_availability_shadows_java") {
-    # Platform checks are broken for Robolectric. See https://crbug.com/1071638.
-    bypass_platform_checks = true
-    testonly = true
+  robolectric_library("chromium_play_services_availability_shadows_java") {
     sources = [ "util/org/chromium/gms/shadows/ShadowChromiumPlayServicesAvailability.java" ]
-    deps = [
-      ":chromium_play_services_availability_java",
-      "//third_party/android_deps:robolectric_all_java",
-    ]
+    deps = [ ":chromium_play_services_availability_java" ]
   }
 }
 
@@ -1463,7 +1457,7 @@
       "//third_party/androidx:androidx_test_monitor_java",
       "//third_party/bouncycastle:bouncycastle_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -1474,7 +1468,7 @@
     enable_bytecode_checks = false
     testonly = true
     deps = [ ":org_robolectric_annotations_java" ]
-    bypass_platform_checks = true
+    is_robolectric = true
 
     # Could also be jetified, but jetification was
     # removed from the build system and 3pp prevents
@@ -1492,7 +1486,7 @@
       ":com_google_guava_guava_java",
       ":org_robolectric_annotations_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -1509,7 +1503,7 @@
       ":org_robolectric_annotations_java",
       ":org_robolectric_pluginapi_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2183,7 +2177,7 @@
       "//third_party/androidx:*",
     ]
     testonly = true
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2206,7 +2200,7 @@
       ":org_robolectric_shadowapi_java",
       ":org_robolectric_utils_reflector_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2223,7 +2217,7 @@
     ]
     testonly = true
     deps = [ ":com_google_guava_guava_java" ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2240,7 +2234,7 @@
     ]
     testonly = true
     deps = [ ":org_robolectric_annotations_java" ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2261,7 +2255,7 @@
       ":org_robolectric_pluginapi_java",
       ":org_robolectric_utils_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2283,7 +2277,7 @@
       ":org_robolectric_pluginapi_java",
       ":org_robolectric_utils_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2310,7 +2304,7 @@
       ":org_robolectric_utils_java",
       ":org_robolectric_utils_reflector_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2330,7 +2324,7 @@
       ":org_robolectric_annotations_java",
       ":org_robolectric_utils_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2362,7 +2356,7 @@
       "//third_party/icu4j:icu4j_java",
       "//third_party/sqlite4java:sqlite4java_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -2384,7 +2378,7 @@
       ":org_ow2_asm_asm_util_java",
       ":org_robolectric_utils_java",
     ]
-    bypass_platform_checks = true
+    is_robolectric = true
   }
 }
 # === Generated Code End ===
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 0a3f4495..93a0419 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -610,9 +610,7 @@
         addPreconditionsOverrideTreatment(sb, dependencyId)
 
         if (dependencyId.startsWith('org_robolectric')) {
-            // Skip platform checks since it depends on
-            // accessibility_test_framework_java which requires_android.
-            sb.append('  bypass_platform_checks = true\n')
+            sb.append('  is_robolectric = true\n')
         }
         if (dependencyExtension == 'aar' &&
                 (dependencyId.startsWith('androidx') || dependencyId.startsWith('com_android_support'))) {
diff --git a/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc b/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
index 1306f1b..3f66e0c 100644
--- a/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
+++ b/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
@@ -45,10 +45,10 @@
   }
 
   const char* proto_data = nullptr;
-  size_t proto_length = 0;
+  int proto_length = 0;
 
   if (!pickle_iterator.ReadData(&proto_data, &proto_length) || !proto_data ||
-      proto_length == 0) {
+      proto_length <= 0) {
     return false;
   }
 
diff --git a/third_party/blink/common/page_state/page_state_serialization.cc b/third_party/blink/common/page_state/page_state_serialization.cc
index e6ccc6c3..901e857 100644
--- a/third_party/blink/common/page_state/page_state_serialization.cc
+++ b/third_party/blink/common/page_state/page_state_serialization.cc
@@ -38,7 +38,7 @@
 void AppendDataToRequestBody(
     const scoped_refptr<network::ResourceRequestBody>& request_body,
     const char* data,
-    size_t data_length) {
+    int data_length) {
   request_body->AppendBytes(data, data_length);
 }
 
@@ -208,11 +208,11 @@
 // PageState serialization format you almost certainly want to add/remove fields
 // in page_state.mojom rather than using these methods.
 
-void WriteData(const void* data, size_t length, SerializeObject* obj) {
+void WriteData(const void* data, int length, SerializeObject* obj) {
   obj->pickle.WriteData(static_cast<const char*>(data), length);
 }
 
-void ReadData(SerializeObject* obj, const void** data, size_t* length) {
+void ReadData(SerializeObject* obj, const void** data, int* length) {
   const char* tmp;
   if (obj->iter.ReadData(&tmp, length)) {
     *data = tmp;
@@ -253,12 +253,12 @@
 
 double ReadReal(SerializeObject* obj) {
   const void* tmp = nullptr;
-  size_t length = 0;
+  int length = 0;
   double value = 0.0;
   ReadData(obj, &tmp, &length);
-  if (length == sizeof(double)) {
+  if (length == static_cast<int>(sizeof(double))) {
     // Use memcpy, as tmp may not be correctly aligned.
-    memcpy(&value, tmp, length);
+    memcpy(&value, tmp, sizeof(double));
   } else {
     obj->parse_error = true;
   }
@@ -295,8 +295,13 @@
 
 // Pickles a std::u16string as <int length>:<char*16 data> tuple>.
 void WriteString(const std::u16string& str, SerializeObject* obj) {
-  obj->pickle.WriteData(reinterpret_cast<const char*>(str.data()),
-                        str.length() * sizeof(char16_t));
+  const char16_t* data = str.data();
+  size_t length_in_bytes = str.length() * sizeof(char16_t);
+
+  CHECK_LT(length_in_bytes,
+           static_cast<size_t>(std::numeric_limits<int>::max()));
+  obj->pickle.WriteInt(length_in_bytes);
+  obj->pickle.WriteBytes(data, length_in_bytes);
 }
 
 // If str is a null optional, this simply pickles a length of -1. Otherwise,
@@ -319,11 +324,11 @@
     return nullptr;
   }
 
-  if (length_in_bytes < 0)  // Not an error!  See WriteString(nullopt).
+  if (length_in_bytes < 0)
     return nullptr;
 
   const char* data;
-  if (!obj->iter.ReadBytes(&data, static_cast<size_t>(length_in_bytes))) {
+  if (!obj->iter.ReadBytes(&data, length_in_bytes)) {
     obj->parse_error = true;
     return nullptr;
   }
@@ -394,7 +399,7 @@
       case network::DataElement::Tag::kBytes: {
         const auto& bytes = element.As<network::DataElementBytes>().bytes();
         WriteInteger(static_cast<int>(HTTPBodyElementType::kTypeData), obj);
-        WriteData(bytes.data(), bytes.size(), obj);
+        WriteData(bytes.data(), static_cast<int>(bytes.size()), obj);
         break;
       }
       case network::DataElement::Tag::kFile: {
@@ -423,9 +428,9 @@
         static_cast<HTTPBodyElementType>(ReadInteger(obj));
     if (type == HTTPBodyElementType::kTypeData) {
       const void* data;
-      size_t length;
+      int length = -1;
       ReadData(obj, &data, &length);
-      if (!obj->parse_error) {
+      if (length >= 0) {
         AppendDataToRequestBody(request_body, static_cast<const char*>(data),
                                 length);
       }
@@ -856,9 +861,9 @@
 
 void ReadMojoPageState(SerializeObject* obj, ExplodedPageState* state) {
   const void* tmp = nullptr;
-  size_t length = 0;
+  int length = 0;
   ReadData(obj, &tmp, &length);
-  DCHECK_GT(length, 0u);
+  DCHECK_GT(length, 0);
   if (obj->parse_error)
     return;
 
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd
index a7d7fb1..c4524da5 100644
--- a/third_party/blink/public/blink_resources.grd
+++ b/third_party/blink/public/blink_resources.grd
@@ -10,6 +10,7 @@
     <includes>
       <!-- GRIT minimizes all CSS and Javascript -->
       <include name="IDR_UASTYLE_HTML_CSS" file="../renderer/core/html/resources/html.css" flattenhtml="true" type="BINDATA" compress="brotli"/>
+      <include name="IDR_UASTYLE_OVERFLOW_REPLACED_CSS" file="../renderer/core/html/resources/overflow_replaced.css" flattenhtml="true" type="BINDATA" compress="brotli"/>
       <include name="IDR_UASTYLE_QUIRKS_CSS" file="../renderer/core/html/resources/quirks.css" type="BINDATA" compress="brotli"/>
       <include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="../renderer/core/css/view-source.css" type="BINDATA" compress="brotli"/>
       <include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="../renderer/core/html/resources/android.css" type="BINDATA" compress="brotli"/>
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 96cc1841..f5752bc 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -1909,6 +1909,16 @@
       # The resulting CSS Supports rule after modification.
       CSSSupports supports
 
+  # Modifies the expression of a scope at-rule.
+  experimental command setScopeText
+    parameters
+      StyleSheetId styleSheetId
+      SourceRange range
+      string text
+    returns
+      # The resulting CSS Scope rule after modification.
+      CSSScope scope
+
   # Modifies the rule selector.
   command setRuleSelector
     parameters
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index ca80bd6..3e225e11f 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -82,6 +82,7 @@
 class WebLocalFrameClient;
 class WebFrameWidget;
 class WebHistoryItem;
+class WebHitTestResult;
 class WebInputMethodController;
 class WebPerformance;
 class WebPlugin;
@@ -903,6 +904,14 @@
       CrossVariantMojoRemote<mojom::StorageAreaInterfaceBase>
           session_storage_area) = 0;
 
+  // Android WebView requires notification of hit tests from blink. It requires
+  // hit tests on touchstart. So this method installs a passive event listener
+  // on touchstart and does a GestureTap hit test providing the results to the
+  // callback.
+  virtual void AddHitTestOnTouchStartCallback(
+      base::RepeatingCallback<void(const blink::WebHitTestResult&)>
+          callback) = 0;
+
  protected:
   explicit WebLocalFrame(mojom::TreeScopeType scope,
                          const LocalFrameToken& frame_token)
diff --git a/third_party/blink/public/web/web_remote_frame.h b/third_party/blink/public/web/web_remote_frame.h
index 5084278..c02632d 100644
--- a/third_party/blink/public/web/web_remote_frame.h
+++ b/third_party/blink/public/web/web_remote_frame.h
@@ -92,6 +92,10 @@
       const base::UnguessableToken& devtools_frame_token,
       WebFrame* opener) = 0;
 
+  // Returns the frame associated with the |frame_token|.
+  BLINK_EXPORT static WebRemoteFrame* FromFrameToken(
+      const RemoteFrameToken& frame_token);
+
   // Set security origin replicated from another process.
   virtual void SetReplicatedOrigin(
       const WebSecurityOrigin&,
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index cad46f2..e952b407 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -327,16 +327,12 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_extendable_message_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_face_detector_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_face_detector_options.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_account_login_request.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_account_login_request.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_request_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_request_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_identity_provider.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_identity_provider.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_tokens.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_tokens.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_fetch_event_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_fetch_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_picker_accept_type.cc",
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index fa52008..67ca3b9 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -2479,8 +2479,7 @@
 
   // Note that while we attach here but we don't detach because the
   // |compositor_timeline| is detached in its destructor.
-  if (compositor_timeline->IsScrollTimeline())
-    document_->AttachCompositorTimeline(compositor_timeline);
+  document_->AttachCompositorTimeline(compositor_timeline);
 }
 
 void Animation::DetachCompositorTimeline() {
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc
index 8884778..74581b4 100644
--- a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc
+++ b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/css/css_selector_list.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_selector.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/html/html_document.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
@@ -100,11 +101,13 @@
                          unsigned expected_result_cache_count,
                          const ExpectedResultCacheEntry (
                              &expected_result_cache_entries)[length]) const {
-    CSSSelectorList selector_list = CSSParser::ParseSelector(
+    CSSSelectorVector selector_vector = CSSParser::ParseSelector(
         MakeGarbageCollected<CSSParserContext>(
             *document, NullURL(), true /* origin_clean */, Referrer(),
             WTF::TextEncoding(), CSSParserContext::kSnapshotProfile),
         nullptr, selector_text);
+    CSSSelectorList selector_list =
+        CSSSelectorList::AdoptSelectorVector(selector_vector);
     const CSSSelector* selector = nullptr;
     for (selector = selector_list.First();
          selector && selector->GetPseudoType() != CSSSelector::kPseudoHas;
diff --git a/third_party/blink/renderer/core/css/container_query.cc b/third_party/blink/renderer/core/css/container_query.cc
index 5aa1a6c..9cb3ae2 100644
--- a/third_party/blink/renderer/core/css/container_query.cc
+++ b/third_party/blink/renderer/core/css/container_query.cc
@@ -3,30 +3,35 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/container_query.h"
-#include "third_party/blink/renderer/core/css/media_query_exp.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
 
 namespace blink {
 
 ContainerSelector::ContainerSelector(AtomicString name,
                                      const MediaQueryExpNode& query)
-    : name_(std::move(name)), feature_flags_(query.CollectFeatureFlags()) {}
+    : name_(std::move(name)) {
+  MediaQueryExpNode::FeatureFlags feature_flags = query.CollectFeatureFlags();
+
+  if (feature_flags & MediaQueryExpNode::kFeatureInlineSize)
+    logical_axes_ |= kLogicalAxisInline;
+  if (feature_flags & MediaQueryExpNode::kFeatureBlockSize)
+    logical_axes_ |= kLogicalAxisBlock;
+  if (feature_flags & MediaQueryExpNode::kFeatureWidth)
+    physical_axes_ |= kPhysicalAxisHorizontal;
+  if (feature_flags & MediaQueryExpNode::kFeatureHeight)
+    physical_axes_ |= kPhysicalAxisVertical;
+}
 
 unsigned ContainerSelector::Type(WritingMode writing_mode) const {
   unsigned type = kContainerTypeNone;
 
-  if (feature_flags_ & MediaQueryExpNode::kFeatureInlineSize)
+  LogicalAxes axes =
+      logical_axes_ | ToLogicalAxes(physical_axes_, writing_mode);
+
+  if ((axes & kLogicalAxisInline).value())
     type |= kContainerTypeInlineSize;
-  if (feature_flags_ & MediaQueryExpNode::kFeatureBlockSize)
+  if ((axes & kLogicalAxisBlock).value())
     type |= kContainerTypeBlockSize;
-  if (feature_flags_ & MediaQueryExpNode::kFeatureWidth) {
-    type |= (IsHorizontalWritingMode(writing_mode) ? kContainerTypeInlineSize
-                                                   : kContainerTypeBlockSize);
-  }
-  if (feature_flags_ & MediaQueryExpNode::kFeatureHeight) {
-    type |= (IsHorizontalWritingMode(writing_mode) ? kContainerTypeBlockSize
-                                                   : kContainerTypeInlineSize);
-  }
 
   return type;
 }
diff --git a/third_party/blink/renderer/core/css/container_query.h b/third_party/blink/renderer/core/css/container_query.h
index 60cb6f5d..abc31fd 100644
--- a/third_party/blink/renderer/core/css/container_query.h
+++ b/third_party/blink/renderer/core/css/container_query.h
@@ -19,11 +19,11 @@
 // https://drafts.csswg.org/css-contain-3/#container-rule
 class CORE_EXPORT ContainerSelector {
  public:
-  using FeatureFlags = MediaQueryExpNode::FeatureFlags;
-
   ContainerSelector() = default;
   ContainerSelector(const ContainerSelector&) = default;
   explicit ContainerSelector(AtomicString name) : name_(std::move(name)) {}
+  explicit ContainerSelector(PhysicalAxes physical_axes)
+      : physical_axes_(physical_axes) {}
   ContainerSelector(AtomicString name, const MediaQueryExpNode&);
 
   const AtomicString& Name() const { return name_; }
@@ -34,7 +34,8 @@
 
  private:
   AtomicString name_;
-  FeatureFlags feature_flags_ = 0;
+  PhysicalAxes physical_axes_{kPhysicalAxisNone};
+  LogicalAxes logical_axes_{kLogicalAxisNone};
 };
 
 class CORE_EXPORT ContainerQuery final
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 50da3fe..1beb82e2 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/container_query_evaluator.cc
@@ -52,14 +52,10 @@
 
 // static
 Element* ContainerQueryEvaluator::FindContainer(
-    const StyleRecalcContext& context,
+    Element* context_element,
     const ContainerSelector& container_selector) {
-  Element* container = context.container;
-  if (!container)
-    return nullptr;
-
   // TODO(crbug.com/1213888): Cache results.
-  for (Element* element = container; element;
+  for (Element* element = context_element; element;
        element = element->ParentOrShadowHostElement()) {
     if (const ComputedStyle* style = element->GetComputedStyle()) {
       if (style->IsContainerForSizeContainerQueries() &&
@@ -75,7 +71,7 @@
 bool ContainerQueryEvaluator::EvalAndAdd(const StyleRecalcContext& context,
                                          const ContainerQuery& query,
                                          MatchResult& match_result) {
-  Element* container = FindContainer(context, query.Selector());
+  Element* container = FindContainer(context.container, query.Selector());
   if (!container)
     return false;
   ContainerQueryEvaluator* evaluator = container->GetContainerQueryEvaluator();
@@ -126,13 +122,13 @@
 
 ContainerQueryEvaluator::Change ContainerQueryEvaluator::ContainerChanged(
     Document& document,
-    const ComputedStyle& style,
+    Element& container,
     PhysicalSize size,
     PhysicalAxes contained_axes) {
   if (size_ == size && contained_axes_ == contained_axes && !font_dirty_)
     return Change::kNone;
 
-  SetData(document, style, size, contained_axes);
+  SetData(document, container, size, contained_axes);
   font_dirty_ = false;
 
   Change change = ComputeChange();
@@ -153,7 +149,7 @@
 }
 
 void ContainerQueryEvaluator::SetData(Document& document,
-                                      const ComputedStyle& style,
+                                      Element& container,
                                       PhysicalSize size,
                                       PhysicalAxes contained_axes) {
   size_ = size;
@@ -166,7 +162,8 @@
   // 'container-type', and when containment is actually applied for that axis.
   //
   // See IsEligibleForSizeContainment (and similar).
-  PhysicalAxes supported_axes = ContainerTypeAxes(style) & contained_axes;
+  PhysicalAxes supported_axes =
+      ContainerTypeAxes(container.ComputedStyleRef()) & contained_axes;
 
   if ((supported_axes & PhysicalAxes(kPhysicalAxisHorizontal)) !=
       PhysicalAxes(kPhysicalAxisNone)) {
@@ -178,8 +175,8 @@
     height = size.height.ToDouble();
   }
 
-  auto* query_values =
-      MakeGarbageCollected<CSSContainerValues>(document, style, width, height);
+  auto* query_values = MakeGarbageCollected<CSSContainerValues>(
+      document, container, width, height);
   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 925c7ea..42d8d4f 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator.h
+++ b/third_party/blink/renderer/core/css/container_query_evaluator.h
@@ -25,7 +25,9 @@
 class CORE_EXPORT ContainerQueryEvaluator final
     : public GarbageCollected<ContainerQueryEvaluator> {
  public:
-  static Element* FindContainer(const StyleRecalcContext& context,
+  // Look for a container query container in the inclusive ancestor
+  // chain of `context_element`.
+  static Element* FindContainer(Element* context_element,
                                 const ContainerSelector&);
   static bool EvalAndAdd(const StyleRecalcContext&,
                          const ContainerQuery&,
@@ -59,7 +61,7 @@
   // Dependent queries are cleared when kUnnamed/kNamed is returned (and left
   // unchanged otherwise).
   Change ContainerChanged(Document&,
-                          const ComputedStyle&,
+                          Element& container,
                           PhysicalSize,
                           PhysicalAxes contained_axes);
 
@@ -72,7 +74,7 @@
   friend class ContainerQueryEvaluatorTest;
 
   void SetData(Document&,
-               const ComputedStyle&,
+               Element& container,
                PhysicalSize,
                PhysicalAxes contained_axes);
   void ClearResults();
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 52e1050..ed7acf41 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
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/html_div_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
@@ -44,6 +45,23 @@
     return &style_rule->GetContainerQuery();
   }
 
+  class TemporaryContainerElement {
+    STACK_ALLOCATED();
+
+   public:
+    explicit TemporaryContainerElement(
+        Document& document,
+        scoped_refptr<const ComputedStyle> style) {
+      element = MakeGarbageCollected<HTMLDivElement>(document);
+      document.body()->AppendChild(element);
+      element->SetComputedStyle(style);
+    }
+
+    ~TemporaryContainerElement() { element->remove(); }
+
+    Element* element;
+  };
+
   bool Eval(String query,
             double width,
             double height,
@@ -52,11 +70,13 @@
     auto style = ComputedStyle::Clone(GetDocument().ComputedStyleRef());
     style->SetContainerType(container_type);
 
+    TemporaryContainerElement temp_container(GetDocument(), style);
+
     ContainerQuery* container_query = ParseContainer(query);
     DCHECK(container_query);
     auto* evaluator = MakeGarbageCollected<ContainerQueryEvaluator>();
     evaluator->ContainerChanged(
-        GetDocument(), *style,
+        GetDocument(), *temp_container.element,
         PhysicalSize(LayoutUnit(width), LayoutUnit(height)), contained_axes);
     return evaluator->Eval(*container_query);
   }
@@ -79,8 +99,10 @@
     style->SetVariableData(AtomicString(custom_property_name), value->Value(),
                            false);
 
+    TemporaryContainerElement temp_container(GetDocument(), style);
+
     auto* evaluator = MakeGarbageCollected<ContainerQueryEvaluator>();
-    evaluator->ContainerChanged(GetDocument(), *style,
+    evaluator->ContainerChanged(GetDocument(), *temp_container.element,
                                 PhysicalSize(LayoutUnit(100), LayoutUnit(100)),
                                 PhysicalAxes{kPhysicalAxisNone});
 
@@ -97,7 +119,10 @@
     auto style = ComputedStyle::Clone(GetDocument().ComputedStyleRef());
     style->SetContainerType(container_type);
 
-    return evaluator->ContainerChanged(GetDocument(), *style, size, axes);
+    TemporaryContainerElement temp_container(GetDocument(), style);
+
+    return evaluator->ContainerChanged(GetDocument(), *temp_container.element,
+                                       size, axes);
   }
 
   bool EvalAndAdd(ContainerQueryEvaluator* evaluator,
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 3162e2b7..1146a16 100644
--- a/third_party/blink/renderer/core/css/css_container_values.cc
+++ b/third_party/blink/renderer/core/css/css_container_values.cc
@@ -11,18 +11,24 @@
 namespace blink {
 
 CSSContainerValues::CSSContainerValues(Document& document,
-                                       const ComputedStyle& style,
+                                       Element& container,
                                        absl::optional<double> width,
                                        absl::optional<double> height)
     : MediaValuesDynamic(document.GetFrame()),
-      style_(&style),
+      style_(container.GetComputedStyle()),
       width_(width),
       height_(height),
-      writing_mode_(style.GetWritingMode()),
+      writing_mode_(container.ComputedStyleRef().GetWritingMode()),
       font_sizes_(CSSToLengthConversionData::FontSizes(
-                      &style,
+                      container.GetComputedStyle(),
                       document.documentElement()->GetComputedStyle())
-                      .Unzoomed()) {}
+                      .Unzoomed()),
+      container_sizes_(container.ParentOrShadowHostElement()) {}
+
+void CSSContainerValues::Trace(Visitor* visitor) const {
+  visitor->Trace(container_sizes_);
+  MediaValuesDynamic::Trace(visitor);
+}
 
 float CSSContainerValues::EmFontSize() const {
   return font_sizes_.Em();
@@ -40,4 +46,12 @@
   return font_sizes_.Ch();
 }
 
+double CSSContainerValues::ContainerWidth() const {
+  return container_sizes_.Width().value_or(SmallViewportWidth());
+}
+
+double CSSContainerValues::ContainerHeight() const {
+  return container_sizes_.Height().value_or(SmallViewportHeight());
+}
+
 }  // 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 eec21a5..db265ea 100644
--- a/third_party/blink/renderer/core/css/css_container_values.h
+++ b/third_party/blink/renderer/core/css/css_container_values.h
@@ -15,7 +15,7 @@
 class CSSContainerValues : public MediaValuesDynamic {
  public:
   explicit CSSContainerValues(Document& document,
-                              const ComputedStyle& style,
+                              Element& container,
                               absl::optional<double> width,
                               absl::optional<double> height);
 
@@ -27,11 +27,17 @@
     return style_.get();
   }
 
+  void Trace(Visitor*) const override;
+
  protected:
   float EmFontSize() const override;
   float RemFontSize() const override;
   float ExFontSize() const override;
   float ChFontSize() const override;
+  // Note that ContainerWidth/ContainerHeight are used to resolve
+  // container *units*. See `container_sizes_`.
+  double ContainerWidth() const override;
+  double ContainerHeight() const override;
   WritingMode GetWritingMode() const override { return writing_mode_; }
 
  private:
@@ -45,6 +51,11 @@
   WritingMode writing_mode_;
   // Container font sizes for resolving relative lengths.
   CSSToLengthConversionData::FontSizes font_sizes_;
+  // Used to resolve container-relative units found in the @container prelude.
+  // Such units refer to container sizes of *ancestor* containers, and must
+  // not be confused with the size of the *current* container (which is stored
+  // in `width_` and `height_`).
+  CSSToLengthConversionData::ContainerSizes container_sizes_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.cc b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
index bb430d5..369ff4e 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.cc
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
@@ -47,6 +47,14 @@
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
+namespace {
+String OverflowForReplacedElementRules() {
+  return RuntimeEnabledFeatures::CSSOverflowForReplacedElementsEnabled()
+             ? UncompressResourceAsASCIIString(
+                   IDR_UASTYLE_OVERFLOW_REPLACED_CSS)
+             : "";
+}
+}  // namespace
 
 CSSDefaultStyleSheets& CSSDefaultStyleSheets::Instance() {
   DEFINE_STATIC_LOCAL(Persistent<CSSDefaultStyleSheets>,
@@ -99,6 +107,7 @@
     : media_controls_style_sheet_loader_(nullptr) {
   // Strict-mode rules.
   String default_rules = UncompressResourceAsASCIIString(IDR_UASTYLE_HTML_CSS) +
+                         OverflowForReplacedElementRules() +
                          LayoutTheme::GetTheme().ExtraDefaultStyleSheet();
 
   default_style_sheet_ = ParseUASheet(default_rules);
@@ -140,6 +149,7 @@
   marker_style_sheet_.Clear();
   // Recreate the default style sheet to clean up possible SVG resources.
   String default_rules = UncompressResourceAsASCIIString(IDR_UASTYLE_HTML_CSS) +
+                         OverflowForReplacedElementRules() +
                          LayoutTheme::GetTheme().ExtraDefaultStyleSheet();
   default_style_sheet_ = ParseUASheet(default_rules);
 
diff --git a/third_party/blink/renderer/core/css/css_property_source_data.h b/third_party/blink/renderer/core/css/css_property_source_data.h
index bc1d7ec..abc4b32f 100644
--- a/third_party/blink/renderer/core/css/css_property_source_data.h
+++ b/third_party/blink/renderer/core/css/css_property_source_data.h
@@ -101,6 +101,8 @@
 
   bool HasSupports() const { return type == StyleRule::kSupports; }
 
+  bool HasScope() const { return type == StyleRule::kScope; }
+
   StyleRule::RuleType type;
 
   // Range of the selector list in the enclosing source.
diff --git a/third_party/blink/renderer/core/css/css_scope_rule.cc b/third_party/blink/renderer/core/css/css_scope_rule.cc
index 052093a..229ccb8 100644
--- a/third_party/blink/renderer/core/css/css_scope_rule.cc
+++ b/third_party/blink/renderer/core/css/css_scope_rule.cc
@@ -46,4 +46,11 @@
   return result.ReleaseString();
 }
 
+void CSSScopeRule::SetPreludeText(const ExecutionContext* execution_context,
+                                  String value) {
+  CSSStyleSheet::RuleMutationScope mutation_scope(this);
+  To<StyleRuleScope>(group_rule_.Get())
+      ->SetPreludeText(execution_context, value);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_scope_rule.h b/third_party/blink/renderer/core/css/css_scope_rule.h
index 10aceacb..a7f37a3 100644
--- a/third_party/blink/renderer/core/css/css_scope_rule.h
+++ b/third_party/blink/renderer/core/css/css_scope_rule.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_grouping_rule.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
@@ -23,6 +24,8 @@
   String PreludeText() const;
   String cssText() const override;
 
+  void SetPreludeText(const ExecutionContext*, String);
+
  private:
   CSSRule::Type GetType() const override { return kScopeRule; }
 };
diff --git a/third_party/blink/renderer/core/css/css_selector_list.cc b/third_party/blink/renderer/core/css/css_selector_list.cc
index e6c1dfe..7fedf6f 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.cc
+++ b/third_party/blink/renderer/core/css/css_selector_list.cc
@@ -51,41 +51,60 @@
   return list;
 }
 
-CSSSelectorList CSSSelectorList::AdoptSelectorVector(
-    CSSSelectorVector& selector_vector) {
+size_t CSSSelectorList::FlattenedSize(
+    const CSSSelectorVector& selector_vector) {
   size_t flattened_size = 0;
-  for (wtf_size_t i = 0; i < selector_vector.size(); ++i) {
-    for (CSSParserSelector* selector = selector_vector[i].get(); selector;
+  for (const std::unique_ptr<blink::CSSParserSelector>& selector_ptr :
+       selector_vector) {
+    for (CSSParserSelector* selector = selector_ptr.get(); selector;
          selector = selector->TagHistory())
       ++flattened_size;
   }
   DCHECK(flattened_size);
+  return flattened_size;
+}
 
-  CSSSelectorList list;
-  list.selector_array_ = std::make_unique<CSSSelector[]>(flattened_size);
+void CSSSelectorList::AdoptSelectorVector(CSSSelectorVector& selector_vector,
+                                          CSSSelector* selector_array,
+                                          size_t flattened_size) {
+  DCHECK_EQ(flattened_size, FlattenedSize(selector_vector));
   wtf_size_t array_index = 0;
-  for (wtf_size_t i = 0; i < selector_vector.size(); ++i) {
-    CSSParserSelector* current = selector_vector[i].get();
+  for (const std::unique_ptr<blink::CSSParserSelector>& selector_ptr :
+       selector_vector) {
+    CSSParserSelector* current = selector_ptr.get();
     while (current) {
       // Move item from the parser selector vector into selector_array_ without
       // invoking destructor (Ugh.)
       CSSSelector* current_selector = current->ReleaseSelector().release();
-      memcpy(&list.selector_array_[array_index], current_selector,
+      memcpy(&selector_array[array_index], current_selector,
              sizeof(CSSSelector));
       WTF::Partitions::FastFree(current_selector);
 
       current = current->TagHistory();
-      DCHECK(!list.selector_array_[array_index].IsLastInSelectorList());
+      DCHECK(!selector_array[array_index].IsLastInSelectorList());
       if (current)
-        list.selector_array_[array_index].SetLastInTagHistory(false);
+        selector_array[array_index].SetLastInTagHistory(false);
       ++array_index;
     }
-    DCHECK(list.selector_array_[array_index - 1].IsLastInTagHistory());
+    DCHECK(selector_array[array_index - 1].IsLastInTagHistory());
   }
   DCHECK_EQ(flattened_size, array_index);
-  list.selector_array_[array_index - 1].SetLastInSelectorList(true);
+  selector_array[array_index - 1].SetLastInSelectorList(true);
   selector_vector.clear();
+}
 
+CSSSelectorList CSSSelectorList::AdoptSelectorVector(
+    CSSSelectorVector& selector_vector) {
+  if (selector_vector.IsEmpty()) {
+    return {};
+  }
+
+  size_t flattened_size = FlattenedSize(selector_vector);
+
+  CSSSelectorList list;
+  list.selector_array_ = std::make_unique<CSSSelector[]>(flattened_size);
+  AdoptSelectorVector(selector_vector, list.selector_array_.get(),
+                      flattened_size);
   return list;
 }
 
diff --git a/third_party/blink/renderer/core/css/css_selector_list.h b/third_party/blink/renderer/core/css/css_selector_list.h
index e9d9137..316412d 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.h
+++ b/third_party/blink/renderer/core/css/css_selector_list.h
@@ -64,6 +64,11 @@
 // Use CSSSelector::TagHistory() and CSSSelector::IsLastInTagHistory()
 // to traverse through each sequence of simple selectors,
 // from .c3 to #ident; from span to .c2; from div to .c1
+//
+// StyleRule stores its selectors in an identical memory layout,
+// but not as part of a CSSSelectorList (see its class comments).
+// It reuses many of the exposed static member functions from CSSSelectorList
+// to provide a subset of its API.
 class CORE_EXPORT CSSSelectorList {
   USING_FAST_MALLOC(CSSSelectorList);
 
@@ -81,8 +86,16 @@
 
   ~CSSSelectorList() = default;
 
+  // Finds out how many elements one would need to allocate for
+  // AdoptSelectorVector(), ie., storing the selector tree as a flattened list.
+  // The returned count is in CSSSelector elements, not bytes.
+  static size_t FlattenedSize(const CSSSelectorVector& selector_vector);
   static CSSSelectorList AdoptSelectorVector(
       CSSSelectorVector& selector_vector);
+  static void AdoptSelectorVector(CSSSelectorVector& selector_vector,
+                                  CSSSelector* selector_array,
+                                  size_t flattened_size);
+
   CSSSelectorList Copy() const;
 
   bool IsValid() const { return !!selector_array_; }
diff --git a/third_party/blink/renderer/core/css/css_selector_watch.cc b/third_party/blink/renderer/core/css/css_selector_watch.cc
index 205b0ab..f20f6ab 100644
--- a/third_party/blink/renderer/core/css/css_selector_watch.cc
+++ b/third_party/blink/renderer/core/css/css_selector_watch.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_selector.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -138,9 +139,9 @@
   }
 }
 
-static bool AllCompound(const CSSSelectorList& selector_list) {
-  for (const CSSSelector* selector = selector_list.First(); selector;
-       selector = selector_list.Next(*selector)) {
+static bool AllCompound(const StyleRule* style_rule) {
+  for (const CSSSelector* selector = style_rule->FirstSelector(); selector;
+       selector = CSSSelectorList::Next(*selector)) {
     if (!selector->IsCompound())
       return false;
   }
@@ -157,17 +158,19 @@
   auto* context = MakeGarbageCollected<CSSParserContext>(
       kUASheetMode, SecureContextMode::kInsecureContext);
   for (const auto& selector : selectors) {
-    CSSSelectorList selector_list =
+    CSSSelectorVector selector_vector =
         CSSParser::ParseSelector(context, nullptr, selector);
-    if (!selector_list.IsValid())
+    if (selector_vector.IsEmpty())
       continue;
 
+    StyleRule* style_rule =
+        StyleRule::Create(selector_vector, callback_property_set);
+
     // Only accept Compound Selectors, since they're cheaper to match.
-    if (!AllCompound(selector_list))
+    if (!AllCompound(style_rule))
       continue;
 
-    watched_callback_selectors_.push_back(MakeGarbageCollected<StyleRule>(
-        std::move(selector_list), callback_property_set));
+    watched_callback_selectors_.push_back(style_rule);
   }
   GetSupplementable()->GetStyleEngine().WatchedSelectorsChanged();
 }
diff --git a/third_party/blink/renderer/core/css/css_style_rule.cc b/third_party/blink/renderer/core/css/css_style_rule.cc
index 7d2790fe..d6dde75 100644
--- a/third_party/blink/renderer/core/css/css_style_rule.cc
+++ b/third_party/blink/renderer/core/css/css_style_rule.cc
@@ -26,8 +26,10 @@
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/css/cssom/declared_style_property_map.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
 #include "third_party/blink/renderer/core/css/style_rule.h"
 #include "third_party/blink/renderer/core/css/style_rule_css_style_declaration.h"
+#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -73,17 +75,23 @@
 
 void CSSStyleRule::setSelectorText(const ExecutionContext* execution_context,
                                    const String& selector_text) {
-  const auto* context = MakeGarbageCollected<CSSParserContext>(
-      ParserContext(execution_context->GetSecureContextMode()));
-  CSSSelectorList selector_list = CSSParser::ParseSelector(
-      context, parentStyleSheet() ? parentStyleSheet()->Contents() : nullptr,
-      selector_text);
-  if (!selector_list.IsValid())
-    return;
-
   CSSStyleSheet::RuleMutationScope mutation_scope(this);
 
-  style_rule_->WrapperAdoptSelectorList(std::move(selector_list));
+  const auto* context = MakeGarbageCollected<CSSParserContext>(
+      ParserContext(execution_context->GetSecureContextMode()));
+  StyleSheetContents* parent_contents =
+      parentStyleSheet() ? parentStyleSheet()->Contents() : nullptr;
+  CSSSelectorVector selector_vector =
+      CSSParser::ParseSelector(context, parent_contents, selector_text);
+  if (selector_vector.IsEmpty())
+    return;
+
+  Member<StyleRule> new_style_rule =
+      StyleRule::Create(selector_vector, std::move(*style_rule_));
+  if (parent_contents) {
+    parent_contents->ReplaceRuleIfExists(style_rule_, new_style_rule);
+  }
+  style_rule_ = new_style_rule;
 
   if (HasCachedSelectorText()) {
     GetSelectorTextCache().erase(this);
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.cc b/third_party/blink/renderer/core/css/css_test_helpers.cc
index 3a61d1c..cb64811a 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.cc
+++ b/third_party/blink/renderer/core/css/css_test_helpers.cc
@@ -223,7 +223,9 @@
   CSSTokenizer tokenizer(string);
   const auto tokens = tokenizer.TokenizeToEOF();
   CSSParserTokenRange range(tokens);
-  return CSSSelectorParser::ParseSelector(range, context, sheet);
+  CSSSelectorVector vector =
+      CSSSelectorParser::ParseSelector(range, context, sheet);
+  return CSSSelectorList::AdoptSelectorVector(vector);
 }
 
 }  // namespace css_test_helpers
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
index a421382a0..89ef898 100644
--- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
+++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 
+#include "third_party/blink/renderer/core/css/container_query.h"
 #include "third_party/blink/renderer/core/css/container_query_evaluator.h"
 #include "third_party/blink/renderer/core/css/css_resolution_units.h"
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -42,35 +43,20 @@
 
 namespace {
 
-PhysicalAxes SupportedAxes(const ComputedStyle& style) {
-  LogicalAxes supported = kLogicalAxisNone;
-  if (style.ContainerType() & kContainerTypeInlineSize)
-    supported |= kLogicalAxisInline;
-  if (style.ContainerType() & kContainerTypeBlockSize)
-    supported |= kLogicalAxisBlock;
-  return ToPhysicalAxes(supported, style.GetWritingMode());
-}
-
-absl::optional<double> FindSizeForContainerAxis(
-    PhysicalAxes requested_axes,
-    const Element* context_element) {
-  for (const Element* element = context_element; element;
-       element = element->ParentOrShadowHostElement()) {
-    auto* evaluator = element->GetContainerQueryEvaluator();
-    if (!evaluator)
-      continue;
-    const ComputedStyle* style = element->GetComputedStyle();
-    if (!style)
-      continue;
-    if ((requested_axes & SupportedAxes(*style)) != requested_axes)
-      continue;
-    evaluator->SetReferencedByUnit();
-    if (requested_axes == PhysicalAxes(kPhysicalAxisHorizontal))
-      return evaluator->Width();
-    DCHECK_EQ(requested_axes, PhysicalAxes(kPhysicalAxisVertical));
-    return evaluator->Height();
-  }
-  return absl::nullopt;
+absl::optional<double> FindSizeForContainerAxis(PhysicalAxes requested_axis,
+                                                Element* context_element) {
+  Element* container = ContainerQueryEvaluator::FindContainer(
+      context_element, ContainerSelector(requested_axis));
+  if (!container)
+    return absl::nullopt;
+  auto* evaluator = container->GetContainerQueryEvaluator();
+  if (!evaluator)
+    return absl::nullopt;
+  evaluator->SetReferencedByUnit();
+  if (requested_axis == kPhysicalAxisHorizontal)
+    return evaluator->Width();
+  DCHECK_EQ(requested_axis, kPhysicalAxisVertical);
+  return evaluator->Height();
 }
 
 void SetHasContainerRelativeUnits(const ComputedStyle* style) {
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
index b8c6da4..6e8e5fb 100644
--- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
+++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
@@ -131,7 +131,7 @@
     // ancestor chain of `context_element`. Optimally, the nearest container-
     // query container is provided, although it's harmless to provide some
     // descendant of that container (we'll just traverse a bit more).
-    explicit ContainerSizes(const Element* context_element)
+    explicit ContainerSizes(Element* context_element)
         : context_element_(context_element) {}
 
     // ContainerSizes::Width/Height is normally computed lazily by looking
@@ -153,7 +153,7 @@
    private:
     void CacheSizeIfNeeded(PhysicalAxes, absl::optional<double>& cache) const;
 
-    Member<const Element> context_element_;
+    Member<Element> context_element_;
     mutable PhysicalAxes cached_physical_axes_{kPhysicalAxisNone};
     mutable absl::optional<double> cached_width_;
     mutable absl::optional<double> cached_height_;
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index c11371b9..f7c46b1 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -43,7 +43,7 @@
                                                   observer);
 }
 
-CSSSelectorList CSSParser::ParseSelector(
+CSSSelectorVector CSSParser::ParseSelector(
     const CSSParserContext* context,
     StyleSheetContents* style_sheet_contents,
     const String& selector) {
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.h b/third_party/blink/renderer/core/css/parser/css_parser.h
index 9277f458..e9d5033 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser.h
@@ -16,6 +16,7 @@
 
 class Color;
 class CSSParserObserver;
+class CSSParserSelector;
 class CSSSelectorList;
 class Element;
 class ExecutionContext;
@@ -28,6 +29,9 @@
 enum class ParseSheetResult;
 enum class SecureContextMode;
 
+// See css_selector_parser.h.
+using CSSSelectorVector = Vector<std::unique_ptr<CSSParserSelector>>;
+
 // This class serves as the public API for the css/parser subsystem
 class CORE_EXPORT CSSParser {
   STATIC_ONLY(CSSParser);
@@ -45,9 +49,9 @@
       CSSDeferPropertyParsing defer_property_parsing =
           CSSDeferPropertyParsing::kNo,
       bool allow_import_rules = true);
-  static CSSSelectorList ParseSelector(const CSSParserContext*,
-                                       StyleSheetContents*,
-                                       const String&);
+  static CSSSelectorVector ParseSelector(const CSSParserContext*,
+                                         StyleSheetContents*,
+                                         const String&);
   static CSSSelectorList ParsePageSelector(const CSSParserContext&,
                                            StyleSheetContents*,
                                            const String&);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 4a66bfcd..8502ee7d 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -1134,37 +1134,8 @@
     observer_->EndRuleHeader(prelude_offset_end);
   }
 
-  absl::optional<CSSSelectorList> from;
-  absl::optional<CSSSelectorList> to;
-
-  prelude.ConsumeWhitespace();
-  if (prelude.Peek().GetType() != kLeftParenthesisToken)
-    return nullptr;
-
-  // <scope-start>
-  {
-    auto block = prelude.ConsumeBlock();
-    from = CSSSelectorParser::ParseScopeBoundary(block, context_, style_sheet_);
-    if (!from)
-      return nullptr;
-  }
-
-  prelude.ConsumeWhitespace();
-
-  // to (<scope-end>)
-  if (css_parsing_utils::ConsumeIfIdent(prelude, "to")) {
-    if (prelude.Peek().GetType() != kLeftParenthesisToken)
-      return nullptr;
-
-    auto block = prelude.ConsumeBlock();
-    to = CSSSelectorParser::ParseScopeBoundary(block, context_, style_sheet_);
-    if (!to)
-      return nullptr;
-  }
-
-  prelude.ConsumeWhitespace();
-
-  if (!prelude.AtEnd())
+  auto* style_scope = StyleScope::Parse(prelude, context_, style_sheet_);
+  if (!style_scope)
     return nullptr;
 
   if (observer_)
@@ -1177,8 +1148,6 @@
   if (observer_)
     observer_->EndRuleBody(stream.Offset());
 
-  auto* style_scope =
-      MakeGarbageCollected<StyleScope>(std::move(*from), std::move(to));
   return MakeGarbageCollected<StyleRuleScope>(*style_scope, rules);
 }
 
@@ -1383,10 +1352,10 @@
     observer_->StartRuleHeader(StyleRule::kStyle, stream.LookAheadOffset());
 
   // Parse the prelude of the style rule
-  CSSSelectorList selector_list = CSSSelectorParser::ConsumeSelector(
+  CSSSelectorVector selector_vector = CSSSelectorParser::ConsumeSelector(
       stream, context_, style_sheet_, observer_);
 
-  if (!selector_list.IsValid()) {
+  if (selector_vector.IsEmpty()) {
     // Read the rest of the prelude if there was an error
     stream.EnsureLookAhead();
     while (!stream.UncheckedAtEnd() &&
@@ -1403,21 +1372,20 @@
   DCHECK_EQ(stream.Peek().GetType(), kLeftBraceToken);
   CSSParserTokenStream::BlockGuard guard(stream);
 
-  if (!selector_list.IsValid())
+  if (selector_vector.IsEmpty())
     return nullptr;  // Parse error, invalid selector list
 
   // TODO(csharrison): How should we lazily parse css that needs the observer?
   if (!observer_ && lazy_state_) {
     DCHECK(style_sheet_);
-    return MakeGarbageCollected<StyleRule>(
-        std::move(selector_list),
-        MakeGarbageCollected<CSSLazyPropertyParserImpl>(stream.Offset() - 1,
-                                                        lazy_state_));
+    return StyleRule::Create(selector_vector,
+                             MakeGarbageCollected<CSSLazyPropertyParserImpl>(
+                                 stream.Offset() - 1, lazy_state_));
   }
   ConsumeDeclarationList(stream, StyleRule::kStyle);
 
-  return MakeGarbageCollected<StyleRule>(
-      std::move(selector_list),
+  return StyleRule::Create(
+      selector_vector,
       CreateCSSPropertyValueSet(parsed_properties_, context_->Mode()));
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_selector.h b/third_party/blink/renderer/core/css/parser/css_parser_selector.h
index 51deaf34..979b879 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_selector.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_selector.h
@@ -46,9 +46,13 @@
   CSSParserSelector& operator=(const CSSParserSelector&) = delete;
   ~CSSParserSelector();
 
+  // Note that on ReleaseSelector() or GetSelector(), you get that single
+  // selector only, not its entire tag history (so TagHistory() will not
+  // make sense until it's put into a CSSSelectorVector).
   std::unique_ptr<CSSSelector> ReleaseSelector() {
     return std::move(selector_);
   }
+  const CSSSelector* GetSelector() const { return selector_.get(); }
 
   CSSSelector::RelationType Relation() const { return selector_->Relation(); }
   void SetValue(const AtomicString& value, bool match_lower_case = false) {
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index cc6af2f..e139317 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -20,6 +20,10 @@
 
 namespace blink {
 
+static void RecordUsageAndDeprecationsOneSelector(
+    const CSSSelector* selector,
+    const CSSParserContext* context);
+
 namespace {
 
 CSSParserTokenRange ConsumeNestedArgument(CSSParserTokenRange& range) {
@@ -43,29 +47,30 @@
 }  // namespace
 
 // static
-CSSSelectorList CSSSelectorParser::ParseSelector(
+CSSSelectorVector CSSSelectorParser::ParseSelector(
     CSSParserTokenRange range,
     const CSSParserContext* context,
     StyleSheetContents* style_sheet) {
   CSSSelectorParser parser(context, style_sheet);
   range.ConsumeWhitespace();
-  CSSSelectorList result = parser.ConsumeComplexSelectorList(range);
+  CSSSelectorVector result = parser.ConsumeComplexSelectorList(range);
   if (!range.AtEnd())
-    return CSSSelectorList();
+    return {};
 
   parser.RecordUsageAndDeprecations(result);
   return result;
 }
 
 // static
-CSSSelectorList CSSSelectorParser::ConsumeSelector(
+CSSSelectorVector CSSSelectorParser::ConsumeSelector(
     CSSParserTokenStream& stream,
     const CSSParserContext* context,
     StyleSheetContents* style_sheet,
     CSSParserObserver* observer) {
   CSSSelectorParser parser(context, style_sheet);
   stream.ConsumeWhitespace();
-  CSSSelectorList result = parser.ConsumeComplexSelectorList(stream, observer);
+  CSSSelectorVector result =
+      parser.ConsumeComplexSelectorList(stream, observer);
   parser.RecordUsageAndDeprecations(result);
   return result;
 }
@@ -82,8 +87,10 @@
   CSSSelectorList result = parser.ConsumeForgivingComplexSelectorList(range);
   if (!range.AtEnd())
     return absl::nullopt;
-
-  parser.RecordUsageAndDeprecations(result);
+  for (const CSSSelector* current = result.First(); current;
+       current = current->TagHistory()) {
+    RecordUsageAndDeprecationsOneSelector(current, context);
+  }
   return result;
 }
 
@@ -107,28 +114,28 @@
                                      StyleSheetContents* style_sheet)
     : context_(context), style_sheet_(style_sheet) {}
 
-CSSSelectorList CSSSelectorParser::ConsumeComplexSelectorList(
+CSSSelectorVector CSSSelectorParser::ConsumeComplexSelectorList(
     CSSParserTokenRange& range) {
   CSSSelectorVector selector_list;
   std::unique_ptr<CSSParserSelector> selector = ConsumeComplexSelector(range);
   if (!selector)
-    return CSSSelectorList();
+    return {};
   selector_list.push_back(std::move(selector));
   while (!range.AtEnd() && range.Peek().GetType() == kCommaToken) {
     range.ConsumeIncludingWhitespace();
     selector = ConsumeComplexSelector(range);
     if (!selector)
-      return CSSSelectorList();
+      return {};
     selector_list.push_back(std::move(selector));
   }
 
   if (failed_parsing_)
-    return CSSSelectorList();
+    return {};
 
-  return CSSSelectorList::AdoptSelectorVector(selector_list);
+  return selector_list;
 }
 
-CSSSelectorList CSSSelectorParser::ConsumeComplexSelectorList(
+CSSSelectorVector CSSSelectorParser::ConsumeComplexSelectorList(
     CSSParserTokenStream& stream,
     CSSParserObserver* observer) {
   CSSSelectorVector selector_list;
@@ -140,12 +147,12 @@
     const wtf_size_t selector_offset_end = stream.LookAheadOffset();
 
     if (stream.UncheckedAtEnd())
-      return CSSSelectorList();
+      return {};
 
     std::unique_ptr<CSSParserSelector> selector =
         ConsumeComplexSelector(complex_selector);
     if (!selector || failed_parsing_ || !complex_selector.AtEnd())
-      return CSSSelectorList();
+      return {};
 
     if (observer)
       observer->ObserveSelector(selector_offset_start, selector_offset_end);
@@ -158,7 +165,7 @@
     stream.ConsumeIncludingWhitespace();
   }
 
-  return CSSSelectorList::AdoptSelectorVector(selector_list);
+  return selector_list;
 }
 
 CSSSelectorList CSSSelectorParser::ConsumeCompoundSelectorList(
@@ -188,7 +195,10 @@
     CSSParserTokenRange& range) {
   if (inside_compound_pseudo_)
     return ConsumeCompoundSelectorList(range);
-  return ConsumeComplexSelectorList(range);
+  CSSSelectorVector result = ConsumeComplexSelectorList(range);
+  if (result.IsEmpty())
+    return {};
+  return CSSSelectorList::AdoptSelectorVector(result);
 }
 
 CSSSelectorList CSSSelectorParser::ConsumeForgivingNestedSelectorList(
@@ -1456,100 +1466,108 @@
 
 }  // namespace
 
+static void RecordUsageAndDeprecationsOneSelector(
+    const CSSSelector* selector,
+    const CSSParserContext* context) {
+  WebFeature feature = WebFeature::kNumberOfFeatures;
+  switch (selector->GetPseudoType()) {
+    case CSSSelector::kPseudoAny:
+      feature = WebFeature::kCSSSelectorPseudoAny;
+      break;
+    case CSSSelector::kPseudoIs:
+      feature = WebFeature::kCSSSelectorPseudoIs;
+      break;
+    case CSSSelector::kPseudoFocusVisible:
+      DCHECK(RuntimeEnabledFeatures::CSSFocusVisibleEnabled());
+      feature = WebFeature::kCSSSelectorPseudoFocusVisible;
+      break;
+    case CSSSelector::kPseudoFocus:
+      feature = WebFeature::kCSSSelectorPseudoFocus;
+      break;
+    case CSSSelector::kPseudoAnyLink:
+      feature = WebFeature::kCSSSelectorPseudoAnyLink;
+      break;
+    case CSSSelector::kPseudoWebkitAnyLink:
+      feature = WebFeature::kCSSSelectorPseudoWebkitAnyLink;
+      break;
+    case CSSSelector::kPseudoWhere:
+      feature = WebFeature::kCSSSelectorPseudoWhere;
+      break;
+    case CSSSelector::kPseudoDefined:
+      feature = WebFeature::kCSSSelectorPseudoDefined;
+      break;
+    case CSSSelector::kPseudoSlotted:
+      feature = WebFeature::kCSSSelectorPseudoSlotted;
+      break;
+    case CSSSelector::kPseudoHost:
+      feature = WebFeature::kCSSSelectorPseudoHost;
+      break;
+    case CSSSelector::kPseudoHostContext:
+      feature = WebFeature::kCSSSelectorPseudoHostContext;
+      break;
+    case CSSSelector::kPseudoFullScreenAncestor:
+      feature = WebFeature::kCSSSelectorPseudoFullScreenAncestor;
+      break;
+    case CSSSelector::kPseudoFullScreen:
+      feature = WebFeature::kCSSSelectorPseudoFullScreen;
+      break;
+    case CSSSelector::kPseudoListBox:
+      feature = WebFeature::kCSSSelectorInternalPseudoListBox;
+      break;
+    case CSSSelector::kPseudoWebKitCustomElement:
+      feature = FeatureForWebKitCustomPseudoElement(selector->Value());
+      break;
+    case CSSSelector::kPseudoSpatialNavigationFocus:
+      feature = WebFeature::kCSSSelectorInternalPseudoSpatialNavigationFocus;
+      break;
+    case CSSSelector::kPseudoReadOnly:
+      feature = WebFeature::kCSSSelectorPseudoReadOnly;
+      break;
+    case CSSSelector::kPseudoReadWrite:
+      feature = WebFeature::kCSSSelectorPseudoReadWrite;
+      break;
+    case CSSSelector::kPseudoDir:
+      DCHECK(RuntimeEnabledFeatures::CSSPseudoDirEnabled());
+      feature = WebFeature::kCSSSelectorPseudoDir;
+      break;
+    case CSSSelector::kPseudoHas:
+      DCHECK(RuntimeEnabledFeatures::CSSPseudoHasEnabled());
+      if (context->IsLiveProfile())
+        feature = WebFeature::kCSSSelectorPseudoHasInLiveProfile;
+      else
+        feature = WebFeature::kCSSSelectorPseudoHasInSnapshotProfile;
+      break;
+    default:
+      break;
+  }
+  if (feature != WebFeature::kNumberOfFeatures) {
+    if (Deprecation::IsDeprecated(feature)) {
+      context->CountDeprecation(feature);
+    } else {
+      context->Count(feature);
+    }
+  }
+  if (selector->Relation() == CSSSelector::kIndirectAdjacent)
+    context->Count(WebFeature::kCSSSelectorIndirectAdjacent);
+  if (selector->SelectorList()) {
+    for (const CSSSelector* current = selector->SelectorList()->First();
+         current; current = current->TagHistory()) {
+      RecordUsageAndDeprecationsOneSelector(current, context);
+    }
+  }
+}
+
 void CSSSelectorParser::RecordUsageAndDeprecations(
-    const CSSSelectorList& selector_list) {
+    const CSSSelectorVector& selector_vector) {
   if (!context_->IsUseCounterRecordingEnabled())
     return;
   if (context_->Mode() == kUASheetMode)
     return;
 
-  for (const CSSSelector* selector = selector_list.First(); selector;
-       selector = CSSSelectorList::Next(*selector)) {
-    for (const CSSSelector* current = selector; current;
+  for (const std::unique_ptr<CSSParserSelector>& selector : selector_vector) {
+    for (const CSSParserSelector* current = selector.get(); current;
          current = current->TagHistory()) {
-      WebFeature feature = WebFeature::kNumberOfFeatures;
-      switch (current->GetPseudoType()) {
-        case CSSSelector::kPseudoAny:
-          feature = WebFeature::kCSSSelectorPseudoAny;
-          break;
-        case CSSSelector::kPseudoIs:
-          feature = WebFeature::kCSSSelectorPseudoIs;
-          break;
-        case CSSSelector::kPseudoFocusVisible:
-          DCHECK(RuntimeEnabledFeatures::CSSFocusVisibleEnabled());
-          feature = WebFeature::kCSSSelectorPseudoFocusVisible;
-          break;
-        case CSSSelector::kPseudoFocus:
-          feature = WebFeature::kCSSSelectorPseudoFocus;
-          break;
-        case CSSSelector::kPseudoAnyLink:
-          feature = WebFeature::kCSSSelectorPseudoAnyLink;
-          break;
-        case CSSSelector::kPseudoWebkitAnyLink:
-          feature = WebFeature::kCSSSelectorPseudoWebkitAnyLink;
-          break;
-        case CSSSelector::kPseudoWhere:
-          feature = WebFeature::kCSSSelectorPseudoWhere;
-          break;
-        case CSSSelector::kPseudoDefined:
-          feature = WebFeature::kCSSSelectorPseudoDefined;
-          break;
-        case CSSSelector::kPseudoSlotted:
-          feature = WebFeature::kCSSSelectorPseudoSlotted;
-          break;
-        case CSSSelector::kPseudoHost:
-          feature = WebFeature::kCSSSelectorPseudoHost;
-          break;
-        case CSSSelector::kPseudoHostContext:
-          feature = WebFeature::kCSSSelectorPseudoHostContext;
-          break;
-        case CSSSelector::kPseudoFullScreenAncestor:
-          feature = WebFeature::kCSSSelectorPseudoFullScreenAncestor;
-          break;
-        case CSSSelector::kPseudoFullScreen:
-          feature = WebFeature::kCSSSelectorPseudoFullScreen;
-          break;
-        case CSSSelector::kPseudoListBox:
-          feature = WebFeature::kCSSSelectorInternalPseudoListBox;
-          break;
-        case CSSSelector::kPseudoWebKitCustomElement:
-          feature = FeatureForWebKitCustomPseudoElement(current->Value());
-          break;
-        case CSSSelector::kPseudoSpatialNavigationFocus:
-          feature =
-              WebFeature::kCSSSelectorInternalPseudoSpatialNavigationFocus;
-          break;
-        case CSSSelector::kPseudoReadOnly:
-          feature = WebFeature::kCSSSelectorPseudoReadOnly;
-          break;
-        case CSSSelector::kPseudoReadWrite:
-          feature = WebFeature::kCSSSelectorPseudoReadWrite;
-          break;
-        case CSSSelector::kPseudoDir:
-          DCHECK(RuntimeEnabledFeatures::CSSPseudoDirEnabled());
-          feature = WebFeature::kCSSSelectorPseudoDir;
-          break;
-        case CSSSelector::kPseudoHas:
-          DCHECK(RuntimeEnabledFeatures::CSSPseudoHasEnabled());
-          if (context_->IsLiveProfile())
-            feature = WebFeature::kCSSSelectorPseudoHasInLiveProfile;
-          else
-            feature = WebFeature::kCSSSelectorPseudoHasInSnapshotProfile;
-          break;
-        default:
-          break;
-      }
-      if (feature != WebFeature::kNumberOfFeatures) {
-        if (Deprecation::IsDeprecated(feature)) {
-          context_->CountDeprecation(feature);
-        } else {
-          context_->Count(feature);
-        }
-      }
-      if (current->Relation() == CSSSelector::kIndirectAdjacent)
-        context_->Count(WebFeature::kCSSSelectorIndirectAdjacent);
-      if (current->SelectorList())
-        RecordUsageAndDeprecations(*current->SelectorList());
+      RecordUsageAndDeprecationsOneSelector(current->GetSelector(), context_);
     }
   }
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.h b/third_party/blink/renderer/core/css/parser/css_selector_parser.h
index b7f5812..25a43a8 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.h
@@ -21,7 +21,11 @@
 class StyleSheetContents;
 
 // SelectorVector is the list of CSS selectors as it is parsed,
-// where each selector can contain others (in a tree).
+// where each selector can contain others (in a tree). Typically,
+// before actual use, you would convert it into a flattened list using
+// CSSSelectorList::AdoptSelectorVector(), but it can be useful to have this
+// temporary form to find out e.g. how many bytes it will occupy
+// (e.g. in StyleRule::Create) before you actually make that allocation.
 using CSSSelectorVector = Vector<std::unique_ptr<CSSParserSelector>>;
 
 // FIXME: We should consider building CSSSelectors directly instead of using
@@ -30,13 +34,15 @@
   STACK_ALLOCATED();
 
  public:
-  static CSSSelectorList ParseSelector(CSSParserTokenRange,
-                                       const CSSParserContext*,
-                                       StyleSheetContents*);
-  static CSSSelectorList ConsumeSelector(CSSParserTokenStream&,
+  // Both ParseSelector() and ConsumeSelector() return an empty list
+  // on error.
+  static CSSSelectorVector ParseSelector(CSSParserTokenRange,
                                          const CSSParserContext*,
-                                         StyleSheetContents*,
-                                         CSSParserObserver*);
+                                         StyleSheetContents*);
+  static CSSSelectorVector ConsumeSelector(CSSParserTokenStream&,
+                                           const CSSParserContext*,
+                                           StyleSheetContents*,
+                                           CSSParserObserver*);
 
   static bool ConsumeANPlusB(CSSParserTokenRange&, std::pair<int, int>&);
 
@@ -68,9 +74,9 @@
 
   // These will all consume trailing comments if successful
 
-  CSSSelectorList ConsumeComplexSelectorList(CSSParserTokenRange&);
-  CSSSelectorList ConsumeComplexSelectorList(CSSParserTokenStream&,
-                                             CSSParserObserver*);
+  CSSSelectorVector ConsumeComplexSelectorList(CSSParserTokenRange&);
+  CSSSelectorVector ConsumeComplexSelectorList(CSSParserTokenStream&,
+                                               CSSParserObserver*);
   CSSSelectorList ConsumeCompoundSelectorList(CSSParserTokenRange&);
   // Consumes a complex selector list if inside_compound_pseudo_ is false,
   // otherwise consumes a compound selector list.
@@ -137,7 +143,7 @@
   static std::unique_ptr<CSSParserSelector>
   SplitCompoundAtImplicitShadowCrossingCombinator(
       std::unique_ptr<CSSParserSelector> compound_selector);
-  void RecordUsageAndDeprecations(const CSSSelectorList&);
+  void RecordUsageAndDeprecations(const CSSSelectorVector&);
   static bool ContainsUnknownWebkitPseudoElements(
       const CSSSelector& complex_selector);
 
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index 85cf671f..4b29a81 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -155,12 +155,12 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_FALSE(list.IsValid());
+    EXPECT_EQ(vector.size(), 0u);
   }
 }
 
@@ -177,12 +177,12 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_TRUE(list.IsValid());
+    EXPECT_GT(vector.size(), 0u);
   }
 }
 
@@ -211,12 +211,12 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_FALSE(list.IsValid());
+    EXPECT_EQ(vector.size(), 0u);
   }
 }
 
@@ -254,15 +254,16 @@
     CSSTokenizer tokenizer(test_case.selector);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_EQ(list.IsValid(), test_case.valid);
+    EXPECT_EQ(!vector.IsEmpty(), test_case.valid);
     if (!test_case.valid)
       continue;
 
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
     ASSERT_TRUE(list.HasOneSelector());
 
     auto* selector = list.First();
@@ -284,12 +285,12 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kUASheetMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_TRUE(list.IsValid());
+    EXPECT_GT(vector.size(), 0u);
   }
 }
 
@@ -302,12 +303,12 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_FALSE(list.IsValid());
+    EXPECT_EQ(vector.size(), 0u);
   }
 }
 
@@ -322,9 +323,9 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
-    EXPECT_FALSE(list.IsValid());
+    EXPECT_EQ(vector.size(), 0u);
   }
 }
 
@@ -339,9 +340,9 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
-    EXPECT_FALSE(list.IsValid());
+    EXPECT_EQ(vector.size(), 0u);
   }
 }
 
@@ -368,8 +369,9 @@
     CSSTokenizer tokenizer(test_case[0]);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
     EXPECT_TRUE(list.IsValid());
     EXPECT_EQ(test_case[1], list.SelectorsText());
   }
@@ -387,9 +389,9 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
-    EXPECT_FALSE(list.IsValid());
+    EXPECT_EQ(vector.size(), 0u);
   }
 }
 
@@ -410,19 +412,19 @@
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
 
-    CSSSelectorList author_list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector author_vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_FALSE(author_list.IsValid());
+    EXPECT_EQ(author_vector.size(), 0u);
 
-    CSSSelectorList ua_list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector ua_vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kUASheetMode, SecureContextMode::kInsecureContext),
         nullptr);
-    EXPECT_TRUE(ua_list.IsValid());
+    EXPECT_GT(ua_vector.size(), 0u);
   }
 }
 
@@ -605,8 +607,10 @@
     CSSTokenizer tokenizer(test_case.input);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
+    EXPECT_GT(vector.size(), 0u);
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
     EXPECT_TRUE(list.IsValid());
     const CSSSelector* selector = list.First();
     ASSERT_TRUE(selector);
@@ -630,8 +634,10 @@
     CSSTokenizer tokenizer(test_case.input);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
+    EXPECT_GT(vector.size(), 0u);
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
     EXPECT_TRUE(list.IsValid());
     const CSSSelector* selector = list.First();
     ASSERT_TRUE(selector);
@@ -640,8 +646,7 @@
 }
 
 TEST(CSSSelectorParserTest, ShadowPartPseudoElementValid) {
-  const char* test_cases[] = {"::part(ident)",
-                              "host::part(ident)",
+  const char* test_cases[] = {"::part(ident)", "host::part(ident)",
                               "host::part(ident):hover"};
 
   for (auto* test_case : test_cases) {
@@ -649,11 +654,12 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
     EXPECT_EQ(test_case, list.SelectorsText());
   }
 }
@@ -669,11 +675,14 @@
     CSSTokenizer tokenizer(test_case);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+    CSSSelectorVector vector = CSSSelectorParser::ParseSelector(
         range,
         MakeGarbageCollected<CSSParserContext>(
             kHTMLStandardMode, SecureContextMode::kInsecureContext),
         nullptr);
+    EXPECT_GT(vector.size(), 0u);
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
+    EXPECT_TRUE(list.IsValid());
     EXPECT_EQ(test_case, list.SelectorsText());
   }
 }
@@ -896,8 +905,9 @@
     CSSTokenizer tokenizer(test_case.input);
     const auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
-    CSSSelectorList list =
+    CSSSelectorVector vector =
         CSSSelectorParser::ParseSelector(range, context, sheet);
+    CSSSelectorList list = CSSSelectorList::AdoptSelectorVector(vector);
     EXPECT_TRUE(list.IsValid());
     const CSSSelector* selector = list.First();
     for (auto sub_expectation : test_case.expectation) {
diff --git a/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc b/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc
index 6aa12e7..8499a4e0 100644
--- a/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_selector.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
@@ -45,10 +46,12 @@
       SelectorFilterParentScope div_scope(*div);
       SelectorFilterParentScope::EnsureParentStackIsPushed();
 
-      CSSSelectorList selectors = CSSParser::ParseSelector(
+      CSSSelectorVector selector_vector = CSSParser::ParseSelector(
           MakeGarbageCollected<CSSParserContext>(
               kHTMLStandardMode, SecureContextMode::kInsecureContext),
           nullptr, "html *, body *, .match *, #myId *");
+      CSSSelectorList selectors =
+          CSSSelectorList::AdoptSelectorVector(selector_vector);
 
       for (const CSSSelector* selector = selectors.First(); selector;
            selector = CSSSelectorList::Next(*selector)) {
@@ -75,10 +78,12 @@
   SelectorFilterRootScope span_scope(GetDocument().getElementById("y"));
   SelectorFilterParentScope::EnsureParentStackIsPushed();
 
-  CSSSelectorList selectors = CSSParser::ParseSelector(
+  CSSSelectorVector selector_vector = CSSParser::ParseSelector(
       MakeGarbageCollected<CSSParserContext>(
           kHTMLStandardMode, SecureContextMode::kInsecureContext),
       nullptr, "html *, body *, div *, span *, .x *, #y *");
+  CSSSelectorList selectors =
+      CSSSelectorList::AdoptSelectorVector(selector_vector);
 
   for (const CSSSelector* selector = selectors.First(); selector;
        selector = CSSSelectorList::Next(*selector)) {
@@ -128,10 +133,12 @@
   SelectorFilterRootScope span_scope(inner);
   SelectorFilterParentScope::EnsureParentStackIsPushed();
 
-  CSSSelectorList selectors = CSSParser::ParseSelector(
+  CSSSelectorVector selector_vector = CSSParser::ParseSelector(
       MakeGarbageCollected<CSSParserContext>(
           kHTMLStandardMode, SecureContextMode::kInsecureContext),
       nullptr, "[Attr] *, [attr] *, [viewbox] *, [VIEWBOX] *");
+  CSSSelectorList selectors =
+      CSSSelectorList::AdoptSelectorVector(selector_vector);
 
   for (const CSSSelector* selector = selectors.First(); selector;
        selector = CSSSelectorList::Next(*selector)) {
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index a4df189..32d7a14 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1568,7 +1568,8 @@
     Element* element,
     const ContainerSelector& container_selector) {
   auto context = StyleRecalcContext::FromAncestors(*element);
-  return ContainerQueryEvaluator::FindContainer(context, container_selector);
+  return ContainerQueryEvaluator::FindContainer(context.container,
+                                                container_selector);
 }
 
 RuleIndexList* StyleResolver::PseudoCSSRulesForElement(
diff --git a/third_party/blink/renderer/core/css/rule_feature_set_test.cc b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
index 9d388fe..8327172e 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/invalidation/invalidation_set.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_selector.h"
 #include "third_party/blink/renderer/core/css/parser/media_query_parser.h"
 #include "third_party/blink/renderer/core/css/rule_set.h"
 #include "third_party/blink/renderer/core/css/style_rule.h"
@@ -44,11 +45,15 @@
   }
 
   static RuleFeatureSet::SelectorPreMatch CollectFeaturesTo(
-      CSSSelectorList selector_list,
+      CSSSelectorVector& selector_vector,
       const StyleScope* style_scope,
       RuleFeatureSet& set) {
-    auto* style_rule = MakeGarbageCollected<StyleRule>(
-        std::move(selector_list),
+    if (selector_vector.IsEmpty()) {
+      return RuleFeatureSet::SelectorPreMatch::kSelectorNeverMatches;
+    }
+
+    auto* style_rule = StyleRule::Create(
+        selector_vector,
         MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode));
     return CollectFeaturesTo(style_rule, style_scope, set);
   }
@@ -76,11 +81,10 @@
   static RuleFeatureSet::SelectorPreMatch CollectFeaturesTo(
       const String& selector_text,
       RuleFeatureSet& set) {
-    CSSSelectorList selector_list = CSSParser::ParseSelector(
+    CSSSelectorVector selector_vector = CSSParser::ParseSelector(
         StrictCSSParserContext(SecureContextMode::kInsecureContext), nullptr,
         selector_text);
-    return CollectFeaturesTo(std::move(selector_list),
-                             nullptr /* style_scope */, set);
+    return CollectFeaturesTo(selector_vector, nullptr /* style_scope */, set);
   }
 
   void ClearFeatures() { rule_feature_set_.Clear(); }
diff --git a/third_party/blink/renderer/core/css/selector_query.cc b/third_party/blink/renderer/core/css/selector_query.cc
index f2d7f9c..9043cfc 100644
--- a/third_party/blink/renderer/core/css/selector_query.cc
+++ b/third_party/blink/renderer/core/css/selector_query.cc
@@ -32,6 +32,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
 #include "third_party/blink/renderer/core/css/selector_checker.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
@@ -475,19 +476,22 @@
   if (it != entries_.end())
     return it->value.get();
 
-  CSSSelectorList selector_list = CSSParser::ParseSelector(
+  CSSSelectorVector selector_vector = CSSParser::ParseSelector(
       MakeGarbageCollected<CSSParserContext>(
           document, document.BaseURL(), true /* origin_clean */, Referrer(),
           WTF::TextEncoding(), CSSParserContext::kSnapshotProfile),
       nullptr, selectors);
 
-  if (!selector_list.First()) {
+  if (selector_vector.IsEmpty()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kSyntaxError,
         "'" + selectors + "' is not a valid selector.");
     return nullptr;
   }
 
+  CSSSelectorList selector_list =
+      CSSSelectorList::AdoptSelectorVector(selector_vector);
+
   const unsigned kMaximumSelectorQueryCacheSize = 256;
   if (entries_.size() == kMaximumSelectorQueryCacheSize)
     entries_.erase(entries_.begin());
diff --git a/third_party/blink/renderer/core/css/selector_query_test.cc b/third_party/blink/renderer/core/css/selector_query_test.cc
index 9306f3d1..0ed4c54a 100644
--- a/third_party/blink/renderer/core/css/selector_query_test.cc
+++ b/third_party/blink/renderer/core/css/selector_query_test.cc
@@ -10,6 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_selector.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
@@ -70,21 +71,24 @@
   document->documentElement()->setInnerHTML(
       "<body><style>span::before { content: 'X' }</style><span></span></body>");
 
-  CSSSelectorList selector_list = CSSParser::ParseSelector(
+  CSSSelectorVector selector_vector = CSSParser::ParseSelector(
       MakeGarbageCollected<CSSParserContext>(
           *document, NullURL(), true /* origin_clean */, Referrer(),
           WTF::TextEncoding(), CSSParserContext::kSnapshotProfile),
       nullptr, "span::before");
+  CSSSelectorList selector_list =
+      CSSSelectorList::AdoptSelectorVector(selector_vector);
   std::unique_ptr<SelectorQuery> query =
       SelectorQuery::Adopt(std::move(selector_list));
   Element* elm = query->QueryFirst(*document);
   EXPECT_EQ(nullptr, elm);
 
-  selector_list = CSSParser::ParseSelector(
+  selector_vector = CSSParser::ParseSelector(
       MakeGarbageCollected<CSSParserContext>(
           *document, NullURL(), true /* origin_clean */, Referrer(),
           WTF::TextEncoding(), CSSParserContext::kSnapshotProfile),
       nullptr, "span");
+  selector_list = CSSSelectorList::AdoptSelectorVector(selector_vector);
   query = SelectorQuery::Adopt(std::move(selector_list));
   elm = query->QueryFirst(*document);
   EXPECT_NE(nullptr, elm);
@@ -99,11 +103,13 @@
 
   document->body()->BeginParsingChildren();
 
-  CSSSelectorList selector_list = CSSParser::ParseSelector(
+  CSSSelectorVector selector_vector = CSSParser::ParseSelector(
       MakeGarbageCollected<CSSParserContext>(
           *document, NullURL(), true /* origin_clean */, Referrer(),
           WTF::TextEncoding(), CSSParserContext::kSnapshotProfile),
       nullptr, "p:last-of-type");
+  CSSSelectorList selector_list =
+      CSSSelectorList::AdoptSelectorVector(selector_vector);
   std::unique_ptr<SelectorQuery> query =
       SelectorQuery::Adopt(std::move(selector_list));
   Element* elm = query->QueryFirst(*document);
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index dc35a5f5..7a49a579 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -2729,7 +2729,7 @@
   DCHECK(evaluator);
 
   ContainerQueryEvaluator::Change query_change = evaluator->ContainerChanged(
-      GetDocument(), style, physical_size, physical_axes);
+      GetDocument(), container, physical_size, physical_axes);
   switch (query_change) {
     case ContainerQueryEvaluator::Change::kNone:
       if (!cq_data->SkippedStyleRecalc())
diff --git a/third_party/blink/renderer/core/css/style_rule.cc b/third_party/blink/renderer/core/css/style_rule.cc
index 658d9c6..b7a878c5 100644
--- a/third_party/blink/renderer/core/css/style_rule.cc
+++ b/third_party/blink/renderer/core/css/style_rule.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/css/parser/container_query_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
 #include "third_party/blink/renderer/core/css/parser/css_supports_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
@@ -351,17 +352,36 @@
          CSSPropertyValueSet::AverageSizeInBytes();
 }
 
-StyleRule::StyleRule(CSSSelectorList selector_list,
+StyleRule::StyleRule(base::PassKey<StyleRule>,
+                     CSSSelectorVector& selector_vector,
+                     size_t flattened_size,
                      CSSPropertyValueSet* properties)
-    : StyleRuleBase(kStyle),
-      selector_list_(std::move(selector_list)),
-      properties_(properties) {}
+    : StyleRuleBase(kStyle), properties_(properties) {
+  CSSSelectorList::AdoptSelectorVector(selector_vector, SelectorArray(),
+                                       flattened_size);
+}
 
-StyleRule::StyleRule(CSSSelectorList selector_list,
+StyleRule::StyleRule(base::PassKey<StyleRule>,
+                     CSSSelectorVector& selector_vector,
+                     size_t flattened_size,
                      CSSLazyPropertyParser* lazy_property_parser)
+    : StyleRuleBase(kStyle), lazy_property_parser_(lazy_property_parser) {
+  CSSSelectorList::AdoptSelectorVector(selector_vector, SelectorArray(),
+                                       flattened_size);
+}
+
+// NOTE: Currently, this move constructor leaves the other object fully intact,
+// since there's no benefit in not doing so.
+StyleRule::StyleRule(base::PassKey<StyleRule>,
+                     CSSSelectorVector& selector_vector,
+                     size_t flattened_size,
+                     StyleRule&& other)
     : StyleRuleBase(kStyle),
-      selector_list_(std::move(selector_list)),
-      lazy_property_parser_(lazy_property_parser) {}
+      properties_(other.properties_),
+      lazy_property_parser_(other.lazy_property_parser_) {
+  CSSSelectorList::AdoptSelectorVector(selector_vector, SelectorArray(),
+                                       flattened_size);
+}
 
 const CSSPropertyValueSet& StyleRule::Properties() const {
   if (!properties_) {
@@ -371,10 +391,26 @@
   return *properties_;
 }
 
-StyleRule::StyleRule(const StyleRule& o)
-    : StyleRuleBase(o),
-      selector_list_(o.selector_list_.Copy()),
-      properties_(o.Properties().MutableCopy()) {}
+StyleRule::StyleRule(const StyleRule& other, size_t flattened_size)
+    : StyleRuleBase(kStyle), properties_(other.Properties().MutableCopy()) {
+  for (unsigned i = 0; i < flattened_size; ++i) {
+    new (&SelectorArray()[i]) CSSSelector(other.SelectorArray()[i]);
+  }
+}
+
+StyleRule::~StyleRule() {
+  // Clean up any RareData that the selectors may be owning.
+  CSSSelector* selector = SelectorArray();
+  for (;;) {
+    bool is_last = selector->IsLastInSelectorList();
+    selector->~CSSSelector();
+    if (is_last) {
+      break;
+    } else {
+      ++selector;
+    }
+  }
+}
 
 MutableCSSPropertyValueSet& StyleRule::MutableProperties() {
   // Ensure properties_ is initialized.
@@ -510,6 +546,14 @@
   StyleRuleGroup::TraceAfterDispatch(visitor);
 }
 
+void StyleRuleScope::SetPreludeText(const ExecutionContext* execution_context,
+                                    String value) {
+  auto* parser_context =
+      MakeGarbageCollected<CSSParserContext>(*execution_context);
+  Vector<CSSParserToken, 32> tokens = CSSTokenizer(value).TokenizeToEOF();
+  style_scope_ = StyleScope::Parse(tokens, parser_context, nullptr);
+}
+
 StyleRuleGroup::StyleRuleGroup(RuleType type,
                                HeapVector<Member<StyleRuleBase>>& adopt_rule)
     : StyleRuleBase(type) {
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h
index 3b203bc3..ed7eca3 100644
--- a/third_party/blink/renderer/core/css/style_rule.h
+++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -22,7 +22,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_RULE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_RULE_H_
 
+#include <limits>
+
 #include "base/memory/scoped_refptr.h"
+#include "base/types/pass_key.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/container_query.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -121,17 +124,73 @@
 // A single rule from a stylesheet. Contains a selector list (one or more
 // complex selectors) and a collection of style properties to be applied where
 // those selectors match. These are output by CSSParserImpl.
+//
+// Note that since this we generate so many StyleRule objects, and all of them
+// have at least one selector, the selector list is not allocated separately as
+// on a CSSSelectorList. Instead, we put the CSSSelectors immediately after the
+// StyleRule object. This both saves memory (since we don't need the pointer,
+// or any of the extra allocation overhead), and makes it likely that the
+// CSSSelectors are on the same cache line as the StyleRule. (On the flip side,
+// it makes it unlikely that the CSSSelector's RareData is on the same cache
+// line as the CSSSelector itself, but it is still overall a good tradeoff
+// for us.) StyleRule provides an API that is a subset of CSSSelectorList,
+// partially implemented using its static member functions.
 class CORE_EXPORT StyleRule : public StyleRuleBase {
  public:
-  // Adopts the selector list
-  StyleRule(CSSSelectorList, CSSPropertyValueSet*);
-  StyleRule(CSSSelectorList, CSSLazyPropertyParser*);
-  StyleRule(const StyleRule&);
+  // Use these to allocate the right amount of memory for the StyleRule.
+  static StyleRule* Create(CSSSelectorVector& selector_vector,
+                           CSSPropertyValueSet* properties) {
+    size_t flattened_size = CSSSelectorList::FlattenedSize(selector_vector);
+    return MakeGarbageCollected<StyleRule>(
+        AdditionalBytes(sizeof(CSSSelector) * flattened_size),
+        base::PassKey<StyleRule>(), selector_vector, flattened_size,
+        properties);
+  }
+  static StyleRule* Create(CSSSelectorVector& selector_vector,
+                           CSSLazyPropertyParser* lazy_property_parser) {
+    size_t flattened_size = CSSSelectorList::FlattenedSize(selector_vector);
+    return MakeGarbageCollected<StyleRule>(
+        AdditionalBytes(sizeof(CSSSelector) * flattened_size),
+        base::PassKey<StyleRule>(), selector_vector, flattened_size,
+        lazy_property_parser);
+  }
+
+  // Creates a StyleRule with the selectors changed (used by setSelectorText()).
+  static StyleRule* Create(CSSSelectorVector& selector_vector,
+                           StyleRule&& other) {
+    size_t flattened_size = CSSSelectorList::FlattenedSize(selector_vector);
+    return MakeGarbageCollected<StyleRule>(
+        AdditionalBytes(sizeof(CSSSelector) * flattened_size),
+        base::PassKey<StyleRule>(), selector_vector, flattened_size,
+        std::move(other));
+  }
+
+  // Constructors. Note that these expect that the StyleRule has been
+  // allocated on the Oilpan heap, with <flattened_size> * sizeof(CSSSelector)
+  // additional bytes after the StyleRule (flattened_size is the number of
+  // selectors). Do not call them directly; they are public only so that
+  // MakeGarbageCollected() can call them. Instead, use Create() above or
+  // Copy() below, as appropriate.
+  StyleRule(base::PassKey<StyleRule>,
+            CSSSelectorVector& selector_vector,
+            size_t flattened_size,
+            CSSPropertyValueSet*);
+  StyleRule(base::PassKey<StyleRule>,
+            CSSSelectorVector& selector_vector,
+            size_t flattened_size,
+            CSSLazyPropertyParser*);
+  StyleRule(base::PassKey<StyleRule>,
+            CSSSelectorVector& selector_vector,
+            size_t flattened_size,
+            StyleRule&&);
+  StyleRule(const StyleRule&, size_t flattened_size);
+  StyleRule(const StyleRule&) = delete;
+  ~StyleRule();
 
   // Partial subset of the CSSSelector API.
-  const CSSSelector* FirstSelector() const { return selector_list_.First(); }
+  const CSSSelector* FirstSelector() const { return SelectorArray(); }
   const CSSSelector& SelectorAt(wtf_size_t index) const {
-    return selector_list_.SelectorAt(index);
+    return SelectorArray()[index];
   }
   wtf_size_t SelectorIndex(const CSSSelector& selector) const {
     return static_cast<wtf_size_t>(&selector - FirstSelector());
@@ -150,12 +209,17 @@
   const CSSPropertyValueSet& Properties() const;
   MutableCSSPropertyValueSet& MutableProperties();
 
-  void WrapperAdoptSelectorList(CSSSelectorList selectors) {
-    selector_list_ = std::move(selectors);
+  StyleRule* Copy() const {
+    const CSSSelector* selector_array = SelectorArray();
+    size_t flattened_size = 1;
+    while (!selector_array[flattened_size - 1].IsLastInSelectorList()) {
+      ++flattened_size;
+    }
+    return MakeGarbageCollected<StyleRule>(
+        AdditionalBytes(sizeof(CSSSelector) * flattened_size), *this,
+        flattened_size);
   }
 
-  StyleRule* Copy() const { return MakeGarbageCollected<StyleRule>(*this); }
-
   static unsigned AverageSizeInBytes();
 
   // Helper function to avoid parsing lazy properties when not needed.
@@ -167,7 +231,15 @@
   friend class CSSLazyParsingTest;
   bool HasParsedProperties() const;
 
-  CSSSelectorList selector_list_;
+  CSSSelector* SelectorArray() {
+    static_assert(alignof(StyleRule) >= alignof(CSSSelector));
+    return reinterpret_cast<CSSSelector*>(this + 1);
+  }
+  const CSSSelector* SelectorArray() const {
+    static_assert(alignof(StyleRule) >= alignof(CSSSelector));
+    return reinterpret_cast<const CSSSelector*>(this + 1);
+  }
+
   mutable Member<CSSPropertyValueSet> properties_;
   mutable Member<CSSLazyPropertyParser> lazy_property_parser_;
 };
@@ -283,6 +355,7 @@
   const HeapVector<Member<StyleRuleBase>>& ChildRules() const {
     return child_rules_;
   }
+  HeapVector<Member<StyleRuleBase>>& ChildRules() { return child_rules_; }
 
   void WrapperInsertRule(unsigned, StyleRuleBase*);
   void WrapperRemoveRule(unsigned);
@@ -311,6 +384,8 @@
 
   const StyleScope& GetStyleScope() const { return *style_scope_; }
 
+  void SetPreludeText(const ExecutionContext*, String);
+
  private:
   Member<const StyleScope> style_scope_;
 };
diff --git a/third_party/blink/renderer/core/css/style_scope.cc b/third_party/blink/renderer/core/css/style_scope.cc
index 794c432c..080c72d5 100644
--- a/third_party/blink/renderer/core/css/style_scope.cc
+++ b/third_party/blink/renderer/core/css/style_scope.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/style_scope.h"
+#include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
+#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
 
@@ -27,4 +30,43 @@
   return *specificity_;
 }
 
+StyleScope* StyleScope::Parse(CSSParserTokenRange prelude,
+                              const CSSParserContext* context,
+                              StyleSheetContents* style_sheet) {
+  absl::optional<CSSSelectorList> from;
+  absl::optional<CSSSelectorList> to;
+
+  prelude.ConsumeWhitespace();
+  if (prelude.Peek().GetType() != kLeftParenthesisToken)
+    return nullptr;
+
+  // <scope-start>
+  {
+    auto block = prelude.ConsumeBlock();
+    from = CSSSelectorParser::ParseScopeBoundary(block, context, style_sheet);
+    if (!from)
+      return nullptr;
+  }
+
+  prelude.ConsumeWhitespace();
+
+  // to (<scope-end>)
+  if (css_parsing_utils::ConsumeIfIdent(prelude, "to")) {
+    if (prelude.Peek().GetType() != kLeftParenthesisToken)
+      return nullptr;
+
+    auto block = prelude.ConsumeBlock();
+    to = CSSSelectorParser::ParseScopeBoundary(block, context, style_sheet);
+    if (!to)
+      return nullptr;
+  }
+
+  prelude.ConsumeWhitespace();
+
+  if (!prelude.AtEnd())
+    return nullptr;
+
+  return MakeGarbageCollected<StyleScope>(std::move(*from), std::move(to));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_scope.h b/third_party/blink/renderer/core/css/style_scope.h
index d059f8a..06ac4226 100644
--- a/third_party/blink/renderer/core/css/style_scope.h
+++ b/third_party/blink/renderer/core/css/style_scope.h
@@ -8,16 +8,21 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_selector_list.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 
 namespace blink {
+class StyleSheetContents;
 
 class CORE_EXPORT StyleScope final : public GarbageCollected<StyleScope> {
  public:
   StyleScope(CSSSelectorList from, absl::optional<CSSSelectorList> to);
   StyleScope(const StyleScope&);
+  static StyleScope* Parse(CSSParserTokenRange prelude,
+                           const CSSParserContext* context,
+                           StyleSheetContents* style_sheet);
 
   void Trace(blink::Visitor* visitor) const { visitor->Trace(parent_); }
 
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.cc b/third_party/blink/renderer/core/css/style_sheet_contents.cc
index 86f9ae0..0f9ed46 100644
--- a/third_party/blink/renderer/core/css/style_sheet_contents.cc
+++ b/third_party/blink/renderer/core/css/style_sheet_contents.cc
@@ -242,6 +242,33 @@
   child_rules_.clear();
 }
 
+static bool ReplaceRuleIfExistsInternal(
+    const StyleRuleBase* old_rule,
+    StyleRuleBase* new_rule,
+    HeapVector<Member<StyleRuleBase>>& child_rules) {
+  for (wtf_size_t i = 0; i < child_rules.size(); ++i) {
+    StyleRuleBase* rule = child_rules[i].Get();
+    if (rule == old_rule) {
+      child_rules[i] = new_rule;
+      return true;
+    }
+    if (IsA<StyleRuleGroup>(rule)) {
+      if (ReplaceRuleIfExistsInternal(old_rule, new_rule,
+                                      To<StyleRuleGroup>(rule)->ChildRules())) {
+        return true;
+      }
+    }
+  }
+
+  // Not found.
+  return false;
+}
+
+void StyleSheetContents::ReplaceRuleIfExists(const StyleRuleBase* old_rule,
+                                             StyleRuleBase* new_rule) {
+  ReplaceRuleIfExistsInternal(old_rule, new_rule, child_rules_);
+}
+
 bool StyleSheetContents::WrapperInsertRule(StyleRuleBase* rule,
                                            unsigned index) {
   DCHECK(is_mutable_);
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.h b/third_party/blink/renderer/core/css/style_sheet_contents.h
index d65304a..b51087a 100644
--- a/third_party/blink/renderer/core/css/style_sheet_contents.h
+++ b/third_party/blink/renderer/core/css/style_sheet_contents.h
@@ -117,6 +117,12 @@
 
   void ClearRules();
 
+  // If the given rule exists, replace it with the new one. This is used when
+  // CSSOM wants to modify the rule but cannot do so without reallocating
+  // (see setCssSelectorText()).
+  void ReplaceRuleIfExists(const StyleRuleBase* old_rule,
+                           StyleRuleBase* new_rule);
+
   // Rules other than @import.
   const HeapVector<Member<StyleRuleBase>>& ChildRules() const {
     return child_rules_;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index a2eb31a..245bcda 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -8572,12 +8572,16 @@
   // 1) SVGElement
   // 2) HTMLFrameOwnerElement
   // 3) HTMLImageElement
-  // 4) HTMLVideoElement
-  // 5) HTMLCanvasElement
   // See https://github.com/w3c/csswg-drafts/issues/7144 for details on enabling
   // ink overflow for replaced elements.
-  return GetPseudoId() == kPseudoIdPageTransitionIncomingImage ||
-         GetPseudoId() == kPseudoIdPageTransitionOutgoingImage;
+  if (GetPseudoId() == kPseudoIdPageTransitionIncomingImage ||
+      GetPseudoId() == kPseudoIdPageTransitionOutgoingImage)
+    return true;
+
+  if (!RuntimeEnabledFeatures::CSSOverflowForReplacedElementsEnabled())
+    return false;
+
+  return IsA<HTMLVideoElement>(this) || IsA<HTMLCanvasElement>(this);
 }
 
 const ComputedStyle* Element::StyleForPositionFallback(unsigned index) {
diff --git a/third_party/blink/renderer/core/exported/web_selector.cc b/third_party/blink/renderer/core/exported/web_selector.cc
index 06ed4d8d..9801403 100644
--- a/third_party/blink/renderer/core/exported/web_selector.cc
+++ b/third_party/blink/renderer/core/exported/web_selector.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/core/css/css_selector_list.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 
 namespace blink {
@@ -42,15 +43,22 @@
   // NOTE: We will always parse the selector in an insecure context mode, if we
   // have selectors which are only parsed in secure contexts, this will need to
   // accept a SecureContextMode as an argument.
-  CSSSelectorList selector_list = CSSParser::ParseSelector(
+  CSSSelectorVector selector_vector = CSSParser::ParseSelector(
       StrictCSSParserContext(SecureContextMode::kInsecureContext), nullptr,
       web_selector);
+  if (selector_vector.IsEmpty()) {
+    // Parse error.
+    return {};
+  }
+
+  CSSSelectorList selector_list =
+      CSSSelectorList::AdoptSelectorVector(selector_vector);
 
   if (restriction == kWebSelectorTypeCompound) {
     for (const CSSSelector* selector = selector_list.First(); selector;
          selector = selector_list.Next(*selector)) {
       if (!selector->IsCompound())
-        return WebString();
+        return {};
     }
   }
   return selector_list.SelectorsText();
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index e97009a1d..92c06d39 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -152,6 +152,7 @@
 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
 #include "third_party/blink/renderer/core/core_initializer.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h"
 #include "third_party/blink/renderer/core/dom/icon_url.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/node_traversal.h"
@@ -176,6 +177,7 @@
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/events/after_print_event.h"
 #include "third_party/blink/renderer/core/events/before_print_event.h"
+#include "third_party/blink/renderer/core/events/touch_event.h"
 #include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
@@ -591,6 +593,64 @@
   }
 };
 
+// Android WebView requires hit testing results on every touch event. This
+// pushes the hit test result to the callback that is registered.
+class TouchStartEventListener : public NativeEventListener {
+ public:
+  explicit TouchStartEventListener(
+      base::RepeatingCallback<void(const blink::WebHitTestResult&)> callback)
+      : callback_(std::move(callback)) {}
+
+  void Invoke(ExecutionContext*, Event* event) override {
+    auto* touch_event = DynamicTo<TouchEvent>(event);
+    if (!touch_event)
+      return;
+    const auto* native_event = touch_event->NativeEvent();
+    if (!native_event)
+      return;
+
+    DCHECK_EQ(WebInputEvent::Type::kTouchStart,
+              native_event->Event().GetType());
+    const auto& web_touch_event =
+        static_cast<const WebTouchEvent&>(native_event->Event());
+
+    if (web_touch_event.touches_length != 1u)
+      return;
+
+    LocalDOMWindow* dom_window = event->currentTarget()->ToLocalDOMWindow();
+    CHECK(dom_window);
+
+    WebGestureEvent tap_event(
+        WebInputEvent::Type::kGestureTap, WebInputEvent::kNoModifiers,
+        base::TimeTicks::Now(), WebGestureDevice::kTouchscreen);
+    // GestureTap is only ever from a touchscreen.
+    tap_event.SetPositionInWidget(
+        web_touch_event.touches[0].PositionInWidget());
+    tap_event.SetPositionInScreen(
+        web_touch_event.touches[0].PositionInScreen());
+    tap_event.SetFrameScale(web_touch_event.FrameScale());
+    tap_event.SetFrameTranslate(web_touch_event.FrameTranslate());
+    tap_event.data.tap.tap_count = 1;
+    tap_event.data.tap.height = tap_event.data.tap.width =
+        std::max(web_touch_event.touches[0].radius_x,
+                 web_touch_event.touches[0].radius_y);
+
+    HitTestResult result =
+        dom_window->GetFrame()
+            ->GetEventHandler()
+            .HitTestResultForGestureEvent(
+                tap_event, HitTestRequest::kReadOnly | HitTestRequest::kActive)
+            .GetHitTestResult();
+
+    result.SetToShadowHostIfInRestrictedShadowRoot();
+
+    callback_.Run(result);
+  }
+
+ private:
+  base::RepeatingCallback<void(const blink::WebHitTestResult&)> callback_;
+};
+
 // WebFrame -------------------------------------------------------------------
 
 static CreateWebFrameWidgetCallback* g_create_web_frame_widget = nullptr;
@@ -2871,6 +2931,18 @@
       *GetFrame(), std::move(session_storage_area));
 }
 
+void WebLocalFrameImpl::AddHitTestOnTouchStartCallback(
+    base::RepeatingCallback<void(const blink::WebHitTestResult&)> callback) {
+  TouchStartEventListener* touch_start_event_listener =
+      MakeGarbageCollected<TouchStartEventListener>(std::move(callback));
+  AddEventListenerOptionsResolved options;
+  options.setPassive(true);
+  options.SetPassiveSpecified(true);
+  options.setCapture(true);
+  GetFrame()->DomWindow()->addEventListener(
+      event_type_names::kTouchstart, touch_start_event_listener, &options);
+}
+
 void WebLocalFrameImpl::SetTargetToCurrentHistoryItem(const WebString& target) {
   current_history_item_.SetTarget(target);
 }
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 6a7c6b7..c7c0c74 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -343,6 +343,9 @@
   void SetSessionStorageArea(
       CrossVariantMojoRemote<mojom::StorageAreaInterfaceBase>
           session_storage_area) override;
+  void AddHitTestOnTouchStartCallback(
+      base::RepeatingCallback<void(const blink::WebHitTestResult&)> callback)
+      override;
 
   // WebNavigationControl overrides:
   bool DispatchBeforeUnloadEvent(bool) override;
diff --git a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
index cb17edb..780effe 100644
--- a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
@@ -44,6 +44,14 @@
 
 namespace blink {
 
+WebRemoteFrame* WebRemoteFrame::FromFrameToken(
+    const RemoteFrameToken& frame_token) {
+  auto* frame = RemoteFrame::FromFrameToken(frame_token);
+  if (!frame)
+    return nullptr;
+  return WebRemoteFrameImpl::FromFrame(*frame);
+}
+
 WebRemoteFrame* WebRemoteFrame::Create(
     mojom::blink::TreeScopeType scope,
     WebRemoteFrameClient* client,
diff --git a/third_party/blink/renderer/core/html/resources/overflow_replaced.css b/third_party/blink/renderer/core/html/resources/overflow_replaced.css
new file mode 100644
index 0000000..a4a42c8
--- /dev/null
+++ b/third_party/blink/renderer/core/html/resources/overflow_replaced.css
@@ -0,0 +1,19 @@
+/* Copyright 2022 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* This sheet is appended to html.css before parsing which means the selectors
+   below are in the default html namespace:
+
+   @namespace "http://www.w3.org/1999/xhtml"
+*/
+
+/*
+   This is in a separate file since the CSS is conditionally applied based on
+   a runtime flag. Merge this with html.css. See crbug.com/1338641.
+*/
+video, canvas {
+   overflow: clip;
+   overflow-clip-margin: content-box;
+}
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 3b7fdb5..d360d86 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -95,6 +95,7 @@
 #include "third_party/blink/renderer/core/inspector/inspector_resource_container.h"
 #include "third_party/blink/renderer/core/inspector/inspector_resource_content_loader.h"
 #include "third_party/blink/renderer/core/inspector/inspector_style_resolver.h"
+#include "third_party/blink/renderer/core/inspector/protocol/css.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
@@ -326,7 +327,8 @@
     kSetMediaRuleText,
     kSetContainerRuleText,
     kSetSupportsRuleText,
-    kSetKeyframeKey
+    kSetKeyframeKey,
+    kSetScopeRuleText,
   };
 
   ModifyRuleAction(Type type,
@@ -366,6 +368,9 @@
       case kSetKeyframeKey:
         return style_sheet_->SetKeyframeKey(new_range_, old_text_, nullptr,
                                             nullptr, exception_state);
+      case kSetScopeRuleText:
+        return style_sheet_->SetScopeRuleText(new_range_, old_text_, nullptr,
+                                              nullptr, exception_state);
       default:
         NOTREACHED();
     }
@@ -398,6 +403,10 @@
         css_rule_ = style_sheet_->SetKeyframeKey(
             old_range_, new_text_, &new_range_, &old_text_, exception_state);
         break;
+      case kSetScopeRuleText:
+        css_rule_ = style_sheet_->SetScopeRuleText(
+            old_range_, new_text_, &new_range_, &old_text_, exception_state);
+        break;
       default:
         NOTREACHED();
     }
@@ -576,6 +585,11 @@
   return DynamicTo<CSSSupportsRule>(rule);
 }
 
+// static
+CSSScopeRule* InspectorCSSAgent::AsCSSScopeRule(CSSRule* rule) {
+  return DynamicTo<CSSScopeRule>(rule);
+}
+
 InspectorCSSAgent::InspectorCSSAgent(
     InspectorDOMAgent* dom_agent,
     InspectedFrames* inspected_frames,
@@ -1667,6 +1681,35 @@
   return InspectorDOMAgent::ToResponse(exception_state);
 }
 
+Response InspectorCSSAgent::setScopeText(
+    const String& style_sheet_id,
+    std::unique_ptr<protocol::CSS::SourceRange> range,
+    const String& text,
+    std::unique_ptr<protocol::CSS::CSSScope>* result) {
+  FrontendOperationScope scope;
+  InspectorStyleSheet* inspector_style_sheet = nullptr;
+  Response response =
+      AssertInspectorStyleSheetForId(style_sheet_id, inspector_style_sheet);
+  if (!response.IsSuccess())
+    return response;
+  SourceRange text_range;
+  response =
+      JsonRangeToSourceRange(inspector_style_sheet, range.get(), &text_range);
+  if (!response.IsSuccess())
+    return response;
+
+  DummyExceptionStateForTesting exception_state;
+  ModifyRuleAction* action = MakeGarbageCollected<ModifyRuleAction>(
+      ModifyRuleAction::kSetScopeRuleText, inspector_style_sheet, text_range,
+      text);
+  bool success = dom_agent_->History()->Perform(action, exception_state);
+  if (success) {
+    CSSScopeRule* rule = InspectorCSSAgent::AsCSSScopeRule(action->TakeRule());
+    *result = BuildScopeObject(rule);
+  }
+  return InspectorDOMAgent::ToResponse(exception_state);
+}
+
 Response InspectorCSSAgent::setSupportsText(
     const String& style_sheet_id,
     std::unique_ptr<protocol::CSS::SourceRange> range,
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.h b/third_party/blink/renderer/core/inspector/inspector_css_agent.h
index 5ad89ca1..ce350e8e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.h
@@ -106,6 +106,7 @@
   static CSSMediaRule* AsCSSMediaRule(CSSRule*);
   static CSSContainerRule* AsCSSContainerRule(CSSRule*);
   static CSSSupportsRule* AsCSSSupportsRule(CSSRule*);
+  static CSSScopeRule* AsCSSScopeRule(CSSRule*);
 
   static void CollectAllDocumentStyleSheets(Document*,
                                             HeapVector<Member<CSSStyleSheet>>&);
@@ -203,6 +204,11 @@
       std::unique_ptr<protocol::CSS::SourceRange>,
       const String& text,
       std::unique_ptr<protocol::CSS::CSSContainerQuery>*) override;
+  protocol::Response setScopeText(
+      const String& style_sheet_id,
+      std::unique_ptr<protocol::CSS::SourceRange>,
+      const String& text,
+      std::unique_ptr<protocol::CSS::CSSScope>*) override;
   protocol::Response setSupportsText(
       const String& style_sheet_id,
       std::unique_ptr<protocol::CSS::SourceRange>,
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
index 968f5582..6f5dc87 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -562,6 +562,43 @@
   return true;
 }
 
+bool VerifyScopeText(Document* document, const String& scope_text) {
+  DEFINE_STATIC_LOCAL(String, bogus_property_name, ("-webkit-boguz-propertee"));
+  auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(
+      ParserContextForDocument(document));
+  CSSRuleSourceDataList* source_data =
+      MakeGarbageCollected<CSSRuleSourceDataList>();
+  String text = "@scope " + scope_text + " { div { " + bogus_property_name +
+                ": none; } }";
+  StyleSheetHandler handler(text, document, source_data);
+  CSSParser::ParseSheetForInspector(ParserContextForDocument(document),
+                                    style_sheet, text, handler);
+
+  // Exactly one scope rule should be parsed.
+  unsigned rule_count = source_data->size();
+  if (rule_count != 1 || source_data->at(0)->type != StyleRule::kScope)
+    return false;
+
+  // Scope rule should have exactly one style rule child.
+  CSSRuleSourceDataList& child_source_data = source_data->at(0)->child_rules;
+  rule_count = child_source_data.size();
+  if (rule_count != 1 || !child_source_data.at(0)->HasProperties())
+    return false;
+
+  // Exactly one property should be in style rule.
+  Vector<CSSPropertySourceData>& property_data =
+      child_source_data.at(0)->property_data;
+  unsigned property_count = property_data.size();
+  if (property_count != 1)
+    return false;
+
+  // Check for the property name.
+  if (property_data.at(0).name != bogus_property_name)
+    return false;
+
+  return true;
+}
+
 void FlattenSourceData(const CSSRuleSourceDataList& data_list,
                        CSSRuleSourceDataList* result) {
   for (CSSRuleSourceData* data : data_list) {
@@ -1334,6 +1371,46 @@
   return supports_rule;
 }
 
+CSSScopeRule* InspectorStyleSheet::SetScopeRuleText(
+    const SourceRange& range,
+    const String& text,
+    SourceRange* new_range,
+    String* old_text,
+    ExceptionState& exception_state) {
+  if (!VerifyScopeText(page_style_sheet_->OwnerDocument(), text)) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kSyntaxError,
+        "Selector or scope rule text is not valid.");
+    return nullptr;
+  }
+
+  CSSRuleSourceData* source_data = FindRuleByHeaderRange(range);
+  if (!source_data || !source_data->HasScope()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotFoundError,
+        "Source range didn't match existing source range");
+    return nullptr;
+  }
+
+  CSSRule* rule = RuleForSourceData(source_data);
+  if (!rule || !rule->parentStyleSheet() ||
+      rule->GetType() != CSSRule::kScopeRule) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotFoundError,
+        "Scope source range didn't match existing source range");
+    return nullptr;
+  }
+
+  CSSScopeRule* scope_rule = InspectorCSSAgent::AsCSSScopeRule(rule);
+  scope_rule->SetPreludeText(
+      page_style_sheet_->OwnerDocument()->GetExecutionContext(), text);
+
+  ReplaceText(source_data->rule_header_range, text, new_range, old_text);
+  OnStyleSheetTextChanged();
+
+  return scope_rule;
+}
+
 CSSRuleSourceData* InspectorStyleSheet::RuleSourceDataAfterSourceRange(
     const SourceRange& source_range) {
   DCHECK(source_data_);
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.h b/third_party/blink/renderer/core/inspector/inspector_style_sheet.h
index b4a6782..9cf25976 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.h
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.h
@@ -30,6 +30,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_source_data.h"
+#include "third_party/blink/renderer/core/css/css_scope_rule.h"
 #include "third_party/blink/renderer/core/css/css_style_declaration.h"
 #include "third_party/blink/renderer/core/inspector/protocol/css.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -170,6 +171,11 @@
                                          SourceRange* new_range,
                                          String* old_selector,
                                          ExceptionState&);
+  CSSScopeRule* SetScopeRuleText(const SourceRange&,
+                                 const String& selector,
+                                 SourceRange* new_range,
+                                 String* old_selector,
+                                 ExceptionState&);
   CSSSupportsRule* SetSupportsRuleText(const SourceRange&,
                                        const String& selector,
                                        SourceRange* new_range,
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index d869003..57535517 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2904,6 +2904,12 @@
     clip_rect = PhysicalBorderBoxRect();
     clip_rect.Contract(BorderBoxOutsets());
     clip_rect.Move(location);
+
+    // Videos need to be pre-snapped so that they line up with the
+    // display_rect and can enable hardware overlays.
+    if (IsVideo())
+      clip_rect = LayoutReplaced::PreSnappedRectForPersistentSizing(clip_rect);
+
     if (HasNonVisibleOverflow()) {
       const auto overflow_clip = GetOverflowClipAxes();
       if (overflow_clip != kOverflowClipBothAxis) {
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc
index e115bf8..2b9c2b0a 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.cc
+++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -1235,4 +1235,19 @@
   return element && element->IsReplacedElementRespectingCSSOverflow();
 }
 
+bool LayoutReplaced::ClipsToContentBox() const {
+  if (!RespectsCSSOverflow())
+    return true;
+
+  // TODO(khushalsagar): There can be more cases where the content clips to
+  // content box. For instance, when padding is 0 and the reference box is the
+  // padding box.
+  const auto& overflow_clip_margin = StyleRef().OverflowClipMargin();
+  return GetOverflowClipAxes() == kOverflowClipBothAxis &&
+         overflow_clip_margin &&
+         overflow_clip_margin->GetReferenceBox() ==
+             StyleOverflowClipMargin::ReferenceBox::kContentBox &&
+         !overflow_clip_margin->GetMargin();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.h b/third_party/blink/renderer/core/layout/layout_replaced.h
index 3d0cb69..eacb3d3a 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.h
+++ b/third_party/blink/renderer/core/layout/layout_replaced.h
@@ -123,6 +123,10 @@
 
   bool RespectsCSSOverflow() const override;
 
+  // Returns true if the content is guarenteed to be clipped to the element's
+  // content box.
+  bool ClipsToContentBox() const;
+
  protected:
   virtual bool CanApplyObjectViewBox() const {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/layout_video.h b/third_party/blink/renderer/core/layout/layout_video.h
index 0f3c437c..7fc587c 100644
--- a/third_party/blink/renderer/core/layout/layout_video.h
+++ b/third_party/blink/renderer/core/layout/layout_video.h
@@ -58,7 +58,8 @@
 
   OverflowClipAxes ComputeOverflowClipAxes() const final {
     NOT_DESTROYED();
-    return kOverflowClipBothAxis;
+    return RespectsCSSOverflow() ? LayoutMedia::ComputeOverflowClipAxes()
+                                 : kOverflowClipBothAxis;
   }
 
  private:
diff --git a/third_party/blink/renderer/core/paint/outline_painter.cc b/third_party/blink/renderer/core/paint/outline_painter.cc
index 258af23e..b2f4ac5 100644
--- a/third_party/blink/renderer/core/paint/outline_painter.cc
+++ b/third_party/blink/renderer/core/paint/outline_painter.cc
@@ -548,7 +548,7 @@
         PaintAutoDarkMode(style_, DarkModeFilter::ElementRole::kBackground));
     if (is_rounded_) {
       context_.StrokePath(center_path, auto_dark_mode,
-                          Path(center_path).length() + width_, width_);
+                          Path(center_path).length(), width_);
     } else {
       // Draw edges one by one instead of the whole path to let the corners
       // have starting/ending dots/dashes.
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index ba0cf66b..7ded4e1 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -2128,7 +2128,16 @@
           clip_rect = To<LayoutBox>(object_).OverflowClipRect(
               context_.current.paint_offset);
         }
-        state.SetClipRect(gfx::RectF(clip_rect), ToSnappedClipRect(clip_rect));
+
+        if (object_.IsLayoutReplaced()) {
+          // TODO(crbug.com/1248598): Should we use non-snapped clip rect for
+          // the first parameter?
+          auto snapped_rect = ToSnappedClipRect(clip_rect);
+          state.SetClipRect(snapped_rect.Rect(), snapped_rect);
+        } else {
+          state.SetClipRect(gfx::RectF(clip_rect),
+                            ToSnappedClipRect(clip_rect));
+        }
 
         state.layout_clip_rect_excluding_overlay_scrollbars =
             FloatClipRect(gfx::RectF(To<LayoutBox>(object_).OverflowClipRect(
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 9ff9e2f..3cca41d2 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1522,6 +1522,41 @@
   EXPECT_CLIP_RECT(FloatRoundedRect(12, 9, 2, 4), properties->OverflowClip());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest, OverflowClipWithBorderRadiusForVideo) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    video {
+      position: fixed;
+      top: 0px;
+      left: 0px;
+      width: 8px;
+      height: 8px;
+      padding: 1px 2px 3px 4px;
+    }
+    </style>
+    <video id="target"></video>
+  )HTML");
+
+  auto* target = GetDocument().getElementById("target");
+  const auto* properties = PaintPropertiesForElement("target");
+  ASSERT_TRUE(properties);
+  ASSERT_TRUE(properties->OverflowClip());
+  EXPECT_CLIP_RECT(FloatRoundedRect(4, 1, 8, 8), properties->OverflowClip());
+  ASSERT_FALSE(properties->InnerBorderRadiusClip());
+
+  target->setAttribute(html_names::kStyleAttr, "border-radius: 5px");
+  UpdateAllLifecyclePhasesForTest();
+  ASSERT_EQ(properties, PaintPropertiesForElement("target"));
+  ASSERT_TRUE(properties->OverflowClip());
+  EXPECT_CLIP_RECT(FloatRoundedRect(4, 1, 8, 8), properties->OverflowClip());
+  ASSERT_TRUE(properties->InnerBorderRadiusClip());
+  EXPECT_CLIP_RECT(FloatRoundedRect(gfx::RectF(4, 1, 8, 8),
+                                    FloatRoundedRect::Radii(
+                                        gfx::SizeF(1, 4), gfx::SizeF(3, 4),
+                                        gfx::SizeF(1, 2), gfx::SizeF(3, 2))),
+                   properties->InnerBorderRadiusClip());
+}
+
 TEST_P(PaintPropertyTreeUpdateTest, ChangingClipPath) {
   GetDocument().GetSettings()->SetPreferCompositingToLCDTextEnabled(false);
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 545a1b1d..54552987 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -574,7 +574,7 @@
 
   // We don't need to pass a fragment here, since we're not actually going to
   // search for any descendant fragment. We've already determined which fragment
-  // that we're going to visit (then one we missed), since we're here.
+  // that we're going to visit (the one we missed), since we're here.
   UpdateContextForOOFContainer(object, context, /* fragment */ nullptr);
 
   if (!object.CanContainAbsolutePositionObjects() ||
diff --git a/third_party/blink/renderer/core/paint/video_painter.cc b/third_party/blink/renderer/core/paint/video_painter.cc
index dff78d3..bcec3cb0 100644
--- a/third_party/blink/renderer/core/paint/video_painter.cc
+++ b/third_party/blink/renderer/core/paint/video_painter.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/paint/image_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
 
@@ -88,14 +89,18 @@
     }
   }
 
-  BoxDrawingRecorder recorder(context, layout_video_, paint_info.phase,
-                              paint_offset);
+  const PhysicalRect visual_rect =
+      layout_video_.ClipsToContentBox() ? content_box_rect : replaced_rect;
+
+  DrawingRecorder recorder(context, layout_video_, paint_info.phase,
+                           ToEnclosingRect(visual_rect));
 
   if (should_display_poster || !force_software_video_paint) {
     // This will display the poster image, if one is present, and otherwise
     // paint nothing.
+
     ImagePainter(layout_video_)
-        .PaintIntoRect(context, replaced_rect, content_box_rect);
+        .PaintIntoRect(context, replaced_rect, visual_rect);
   } else {
     cc::PaintFlags video_flags = context.FillFlags();
     video_flags.setColor(SK_ColorBLACK);
diff --git a/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc
index 006bf33..533edd4 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc
@@ -554,6 +554,53 @@
   webotp_service->Abort();
 }
 
+// Abort an ongoing FederatedCredential login() operation.
+void AbortFederatedCredentialRequest(ScriptState* script_state) {
+  if (!script_state->ContextIsValid())
+    return;
+
+  auto* auth_request =
+      CredentialManagerProxy::From(script_state)->FederatedAuthRequest();
+  auth_request->CancelTokenRequest();
+}
+
+void OnRequestIdToken(ScriptPromiseResolver* resolver,
+                      const KURL& provider_url,
+                      const String& client_id,
+                      const String& hint,
+                      const CredentialRequestOptions* options,
+                      RequestIdTokenStatus status,
+                      const WTF::String& id_token) {
+  switch (status) {
+    case RequestIdTokenStatus::kErrorTooManyRequests: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError,
+          "Only one navigator.credentials.get request may be outstanding at "
+          "one time."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorCanceled: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError, "The request has been aborted."));
+      return;
+    }
+    case RequestIdTokenStatus::kError: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError, "Error retrieving an id token."));
+      return;
+    }
+    case RequestIdTokenStatus::kSuccess: {
+      FederatedCredential* credential = FederatedCredential::Create(
+          provider_url, client_id, hint, options, id_token);
+      resolver->Resolve(credential);
+      return;
+    }
+    default: {
+      NOTREACHED();
+    }
+  }
+}
+
 void OnStoreComplete(std::unique_ptr<ScopedPromiseResolver> scoped_resolver) {
   auto* resolver = scoped_resolver->Release();
   AssertSecurityRequirementsBeforeResponse(
@@ -1173,6 +1220,7 @@
             provider->GetAsFederatedIdentityProvider();
         KURL provider_url(federated_identity_provider->url());
         String client_id = federated_identity_provider->clientId();
+        String nonce = federated_identity_provider->getNonceOr("");
 
         if (!provider_url.IsValid() || client_id == "") {
           resolver->Reject(MakeGarbageCollected<DOMException>(
@@ -1187,10 +1235,26 @@
           return promise;
         }
 
-        FederatedCredential* credential = FederatedCredential::Create(
-            provider_url, client_id, federated_identity_provider->getHintOr(""),
-            options);
-        resolver->Resolve(credential);
+        DCHECK(options->federated()->hasPreferAutoSignIn());
+        if (options->hasSignal()) {
+          if (options->signal()->aborted()) {
+            resolver->Reject(MakeGarbageCollected<DOMException>(
+                DOMExceptionCode::kAbortError, "Request has been aborted."));
+            return promise;
+          }
+          options->signal()->AddAlgorithm(WTF::Bind(
+              &AbortFederatedCredentialRequest, WrapPersistent(script_state)));
+        }
+        bool prefer_auto_sign_in = options->federated()->preferAutoSignIn();
+        auto* auth_request =
+            CredentialManagerProxy::From(script_state)->FederatedAuthRequest();
+
+        auth_request->RequestIdToken(
+            provider_url, client_id, nonce, prefer_auto_sign_in,
+            WTF::Bind(&OnRequestIdToken, WrapPersistent(resolver), provider_url,
+                      client_id, federated_identity_provider->getHintOr(""),
+                      WrapPersistent(options)));
+
         return promise;
       }
     }
diff --git a/third_party/blink/renderer/modules/credentialmanagement/federated_credential.cc b/third_party/blink/renderer/modules/credentialmanagement/federated_credential.cc
index 7c383613..9741cc7e 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/federated_credential.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/federated_credential.cc
@@ -7,20 +7,14 @@
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_credential_creation_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_credential_request_options.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_federated_account_login_request.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_logout_rps_request.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_federated_credential_request_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_federated_identity_provider.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_federated_tokens.h"
-#include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h"
 #include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.h"
-#include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 
@@ -41,56 +35,6 @@
   kMaxValue = kFailedOrigin
 };
 
-// Abort an ongoing FederatedCredential login() operation.
-void AbortFederatedCredentialRequest(ScriptState* script_state) {
-  if (!script_state->ContextIsValid())
-    return;
-
-  auto* auth_request =
-      CredentialManagerProxy::From(script_state)->FederatedAuthRequest();
-  auth_request->CancelTokenRequest();
-}
-
-void OnRequestIdToken(ScriptPromiseResolver* resolver,
-                      RequestIdTokenStatus status,
-                      const WTF::String& id_token) {
-  // TODO(yigu): we should reject certain promise with unified message and delay
-  // to avoid fingerprinting.
-  switch (status) {
-    case RequestIdTokenStatus::kApprovalDeclined: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kAbortError, "User declined the sign-in attempt."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorTooManyRequests: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kAbortError,
-          "Only one navigator.credentials.get request may be outstanding at "
-          "one time."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorCanceled: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kAbortError, "The request has been aborted."));
-      return;
-    }
-    case RequestIdTokenStatus::kError: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNetworkError, "Error retrieving an id token."));
-      return;
-    }
-    case RequestIdTokenStatus::kSuccess: {
-      FederatedTokens* tokens = FederatedTokens::Create();
-      tokens->setIdToken(id_token);
-      resolver->Resolve(tokens);
-      return;
-    }
-    default: {
-      NOTREACHED();
-    }
-  }
-}
-
 void OnLogoutRpsResponse(ScriptPromiseResolver* resolver,
                          LogoutRpsStatus status) {
   // TODO(kenrb); There should be more thought put into how this API works.
@@ -152,9 +96,10 @@
     const KURL& provider_url,
     const String& client_id,
     const String& hint,
-    const CredentialRequestOptions* options) {
+    const CredentialRequestOptions* options,
+    const String& id_token) {
   return MakeGarbageCollected<FederatedCredential>(provider_url, client_id,
-                                                   hint, options);
+                                                   hint, options, id_token);
 }
 
 bool FederatedCredential::IsRejectingPromiseDueToCSP(
@@ -210,12 +155,14 @@
     const KURL& provider_url,
     const String& client_id,
     const String& hint,
-    const CredentialRequestOptions* options)
+    const CredentialRequestOptions* options,
+    const String& id_token)
     : Credential(/* id = */ hint, kFederatedCredentialType),
       provider_origin_(SecurityOrigin::Create(provider_url)),
       provider_url_(provider_url),
       client_id_(client_id),
-      options_(options) {}
+      options_(options),
+      id_token_(id_token) {}
 
 void FederatedCredential::Trace(Visitor* visitor) const {
   Credential::Trace(visitor);
@@ -226,42 +173,22 @@
   return true;
 }
 
-ScriptPromise FederatedCredential::login(
-    ScriptState* script_state,
-    FederatedAccountLoginRequest* request) {
+// Temporary dummy login method to preserve version detection.
+ScriptPromise FederatedCredential::login(ScriptState* script_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
+
   if (provider_url_.IsEmpty() || !options_) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError,
         "FederatedCredential object must be created by "
-        "navigator.credentials.get for login"));
+        "navigator.credentials.get for login. Note that "
+        "navigator.credentials.login has been merged with "
+        "navigator.credentials.get."));
     return promise;
   }
 
-  ContentSecurityPolicy* policy =
-      resolver->GetExecutionContext()
-          ->GetContentSecurityPolicyForCurrentWorld();
-  // We disallow redirects (in idp_network_request_manager.cc), so it is
-  // enough to check the initial URL here.
-  if (IsRejectingPromiseDueToCSP(policy, resolver, provider_url_))
-    return promise;
-  if (request->hasSignal()) {
-    if (request->signal()->aborted()) {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kAbortError, "Request has been aborted."));
-      return promise;
-    }
-    request->signal()->AddAlgorithm(WTF::Bind(&AbortFederatedCredentialRequest,
-                                              WrapPersistent(script_state)));
-  }
-  DCHECK(options_->federated()->hasPreferAutoSignIn());
-  bool prefer_auto_sign_in = options_->federated()->preferAutoSignIn();
-  auto* auth_request =
-      CredentialManagerProxy::From(script_state)->FederatedAuthRequest();
-  auth_request->RequestIdToken(
-      provider_url_, client_id_, request->getNonceOr(""), prefer_auto_sign_in,
-      WTF::Bind(&OnRequestIdToken, WrapPersistent(resolver)));
+  resolver->Resolve(this);
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/credentialmanagement/federated_credential.h b/third_party/blink/renderer/modules/credentialmanagement/federated_credential.h
index 51555ef5..36828a21 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/federated_credential.h
+++ b/third_party/blink/renderer/modules/credentialmanagement/federated_credential.h
@@ -16,7 +16,6 @@
 namespace blink {
 
 class CredentialRequestOptions;
-class FederatedAccountLoginRequest;
 class FederatedCredentialInit;
 
 class MODULES_EXPORT FederatedCredential final : public Credential {
@@ -34,7 +33,8 @@
   static FederatedCredential* Create(const KURL& provider_url,
                                      const String& client_id,
                                      const String& hint,
-                                     const CredentialRequestOptions* options);
+                                     const CredentialRequestOptions* options,
+                                     const String& id_token);
 
   static bool IsRejectingPromiseDueToCSP(ContentSecurityPolicy* policy,
                                          ScriptPromiseResolver* resolver,
@@ -48,7 +48,8 @@
   FederatedCredential(const KURL& provider_url,
                       const String& client_id,
                       const String& hint,
-                      const CredentialRequestOptions* options);
+                      const CredentialRequestOptions* options,
+                      const String& id_token);
 
   void Trace(Visitor*) const override;
 
@@ -66,14 +67,14 @@
   }
   const String& name() const { return name_; }
   const KURL& iconURL() const { return icon_url_; }
+  const String& idToken() const { return id_token_; }
   const String& protocol() const {
     // TODO(mkwst): This is a stub, as we don't yet have any support on the
     // Chromium-side.
     return g_empty_string;
   }
 
-  ScriptPromise login(ScriptState* script_state,
-                      FederatedAccountLoginRequest* request);
+  ScriptPromise login(ScriptState* script_state);
 
   static ScriptPromise logoutRps(
       ScriptState*,
@@ -86,6 +87,7 @@
   const KURL provider_url_;
   const String client_id_;
   Member<const CredentialRequestOptions> options_;
+  const String id_token_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/credentialmanagement/federated_credential.idl b/third_party/blink/renderer/modules/credentialmanagement/federated_credential.idl
index 1eccd81..73a34d0 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/federated_credential.idl
+++ b/third_party/blink/renderer/modules/credentialmanagement/federated_credential.idl
@@ -4,17 +4,6 @@
 
 // https://w3c.github.io/webappsec-credential-management/#federatedcredential
 
-[Exposed=Window, SecureContext]
-dictionary FederatedAccountLoginRequest {
-  AbortSignal signal;
-  DOMString nonce;
-};
-
-[Exposed=Window, SecureContext]
-dictionary FederatedTokens {
-  USVString idToken;
-};
-
 [
     Exposed=Window,
     SecureContext
@@ -22,9 +11,12 @@
     [RaisesException] constructor(FederatedCredentialInit data);
     readonly attribute USVString provider;
 
-    // Gets an idToken from the IDP.
+    // Temporary dummy login method to preserve version detection.
     [RuntimeEnabled=FedCm, CallWith=ScriptState]
-    Promise<FederatedTokens> login(optional FederatedAccountLoginRequest request = {});
+    Promise<FederatedCredential> login();
+
+    // https://fedidcg.github.io/FedCM/#dom-id_token_endpoint_response-id_token
+    [RuntimeEnabled=FedCm] readonly attribute USVString idToken;
 
     // Allows IDPs to logout the user out of all of the logged in RPs.
     [RuntimeEnabled=FedCmIdpSignout, CallWith=ScriptState, MeasureAs=FedCmLogoutRps]
diff --git a/third_party/blink/renderer/modules/credentialmanagement/federated_identity_provider.idl b/third_party/blink/renderer/modules/credentialmanagement/federated_identity_provider.idl
index 970e910..d715305 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/federated_identity_provider.idl
+++ b/third_party/blink/renderer/modules/credentialmanagement/federated_identity_provider.idl
@@ -10,4 +10,5 @@
   required USVString clientId;
   // Optional hint for the account ID.
   USVString hint;
+  USVString nonce;
 };
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
index 91f2274..9115d243 100644
--- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
@@ -121,30 +121,34 @@
 // 20px (r = .5), the corner radius of the shadow shape will be
 // 10px + 20px × (1 + (.5 - 1)^3) = 27.5px rather than 30px. This adjustment
 // is applied independently to the radii in each dimension.
-static void OutsetCornerForMarginOrShadow(gfx::SizeF& corner, float outset) {
-  if (corner.IsZero() || outset == 0)
+static void OutsetCornerForMarginOrShadow(gfx::SizeF& corner,
+                                          float width_outset,
+                                          float height_outset) {
+  if (corner.IsZero() || (width_outset == 0 && height_outset == 0))
     return;
 
   float width_factor = 1;
-  if (corner.width() < abs(outset))
-    width_factor = 1 + pow(corner.width() / abs(outset) - 1, 3);
+  if (corner.width() < abs(width_outset))
+    width_factor = 1 + pow(corner.width() / abs(width_outset) - 1, 3);
 
   float height_factor = 1;
-  if (corner.height() == corner.width())
+  if (corner.height() == corner.width() && width_outset == height_outset)
     height_factor = width_factor;
-  else if (corner.height() < abs(outset))
-    height_factor = 1 + pow(corner.height() / abs(outset) - 1, 3);
+  else if (corner.height() < abs(height_outset))
+    height_factor = 1 + pow(corner.height() / abs(height_outset) - 1, 3);
 
-  corner.set_width(std::max(corner.width() + width_factor * outset, 0.f));
-  corner.set_height(std::max(corner.height() + height_factor * outset, 0.f));
+  corner.set_width(std::max(corner.width() + width_factor * width_outset, 0.f));
+  corner.set_height(
+      std::max(corner.height() + height_factor * height_outset, 0.f));
 }
 
 void FloatRoundedRect::Radii::OutsetForMarginOrShadow(
     const gfx::OutsetsF& outsets) {
-  OutsetCornerForMarginOrShadow(top_left_, outsets.top());
-  OutsetCornerForMarginOrShadow(top_right_, outsets.right());
-  OutsetCornerForMarginOrShadow(bottom_left_, outsets.bottom());
-  OutsetCornerForMarginOrShadow(bottom_right_, outsets.right());
+  OutsetCornerForMarginOrShadow(top_left_, outsets.left(), outsets.top());
+  OutsetCornerForMarginOrShadow(top_right_, outsets.right(), outsets.top());
+  OutsetCornerForMarginOrShadow(bottom_left_, outsets.left(), outsets.bottom());
+  OutsetCornerForMarginOrShadow(bottom_right_, outsets.right(),
+                                outsets.bottom());
 }
 
 void FloatRoundedRect::Radii::OutsetForShapeMargin(float outset) {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 1fd8139a..7ad6c511 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -458,8 +458,10 @@
         // In non-OOPR mode we need to update the client side SkSurface with the
         // copied texture. Recreating SkSurface here matches the GPU process
         // behaviour that will happen in OOPR mode.
-        if (!use_oop_rasterization_)
+        if (!use_oop_rasterization_) {
+          EnsureWriteAccess();
           GetSkSurface();
+        }
       } else {
         EnsureWriteAccess();
         if (surface_) {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 8f967d93..18dced4 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -1076,7 +1076,8 @@
     return;
 
   DrawPath(path_to_stroke.GetSkPath(),
-           ImmutableState()->StrokeFlags(length, dash_thickness),
+           ImmutableState()->StrokeFlags(length, dash_thickness,
+                                         path_to_stroke.IsClosed()),
            auto_dark_mode);
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_state.cc b/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
index a4e33f83ba..96e3d3d 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
@@ -50,9 +50,10 @@
 
 const cc::PaintFlags& GraphicsContextState::StrokeFlags(
     const int stroked_path_length,
-    const int dash_thickness) const {
+    const int dash_thickness,
+    const bool closed_path) const {
   stroke_data_.SetupPaintDashPathEffect(&stroke_flags_, stroked_path_length,
-                                        dash_thickness);
+                                        dash_thickness, closed_path);
   return stroke_flags_;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_state.h b/third_party/blink/renderer/platform/graphics/graphics_context_state.h
index 54948d1..1a8d9bb 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_state.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_state.h
@@ -61,7 +61,8 @@
   // cc::PaintFlags objects that reflect the current state. If the length of the
   // path to be stroked is known, pass it in for correct dash or dot placement.
   const cc::PaintFlags& StrokeFlags(const int stroked_path_length = 0,
-                                    const int dash_thickness = 0) const;
+                                    const int dash_thickness = 0,
+                                    const bool closed_path = false) const;
   const cc::PaintFlags& FillFlags() const { return fill_flags_; }
 
   uint16_t SaveCount() const { return save_count_; }
diff --git a/third_party/blink/renderer/platform/graphics/stroke_data.cc b/third_party/blink/renderer/platform/graphics/stroke_data.cc
index 98fbf50..c8787444 100644
--- a/third_party/blink/renderer/platform/graphics/stroke_data.cc
+++ b/third_party/blink/renderer/platform/graphics/stroke_data.cc
@@ -54,21 +54,20 @@
   dash_ = SkDashPathEffect::Make(intervals.get(), count, dash_offset);
 }
 
-void StrokeData::SetupPaint(cc::PaintFlags* flags,
-                            const int length,
-                            const int dash_thickness) const {
+void StrokeData::SetupPaint(cc::PaintFlags* flags) const {
   flags->setStyle(cc::PaintFlags::kStroke_Style);
   flags->setStrokeWidth(SkFloatToScalar(thickness_));
   flags->setStrokeCap(line_cap_);
   flags->setStrokeJoin(line_join_);
   flags->setStrokeMiter(SkFloatToScalar(miter_limit_));
 
-  SetupPaintDashPathEffect(flags, length, dash_thickness);
+  SetupPaintDashPathEffect(flags);
 }
 
 void StrokeData::SetupPaintDashPathEffect(cc::PaintFlags* flags,
                                           const int length,
-                                          const int dash_thickness) const {
+                                          const int dash_thickness,
+                                          const bool closed_path) const {
   int dash_width = dash_thickness ? dash_thickness : thickness_;
   if (dash_) {
     flags->setPathEffect(dash_);
@@ -82,18 +81,23 @@
     if (length <= dash_length * 2) {
       // No space for dashes
       flags->setPathEffect(nullptr);
-    } else if (length <= 2 * dash_length + gap_length) {
-      // Exactly 2 dashes proportionally sized
-      float multiplier = length / (2 * dash_length + gap_length);
-      SkScalar intervals[2] = {dash_length * multiplier,
-                               gap_length * multiplier};
-      flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
     } else {
-      float gap = gap_length;
-      if (style_ == kDashedStroke)
-        gap = SelectBestDashGap(length, dash_length, gap_length);
-      SkScalar intervals[2] = {dash_length, gap};
-      flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+      float two_dashes_with_gap_length = 2 * dash_length + gap_length;
+      if (closed_path)
+        two_dashes_with_gap_length += gap_length;
+      if (length <= two_dashes_with_gap_length) {
+        // Exactly 2 dashes proportionally sized
+        float multiplier = length / two_dashes_with_gap_length;
+        SkScalar intervals[2] = {dash_length * multiplier,
+                                 gap_length * multiplier};
+        flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+      } else {
+        float gap = gap_length;
+        if (style_ == kDashedStroke)
+          gap = SelectBestDashGap(length, dash_length, gap_length, closed_path);
+        SkScalar intervals[2] = {dash_length, gap};
+        flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+      }
     }
   } else if (style_ == kDottedStroke) {
     flags->setStrokeCap(cc::PaintFlags::Cap::kRound_Cap);
@@ -111,7 +115,7 @@
     // even if that dot is a little inside the true endpoint. Without it
     // we can drop the end dot due to rounding along the line.
     static const float kEpsilon = 1.0e-2f;
-    float gap = SelectBestDashGap(length, dash_width, dash_width);
+    float gap = SelectBestDashGap(length, dash_width, dash_width, closed_path);
     SkScalar intervals[2] = {0, gap + dash_width - kEpsilon};
     flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
   } else {
@@ -125,16 +129,18 @@
 
 float StrokeData::SelectBestDashGap(float stroke_length,
                                     float dash_length,
-                                    float gap_length) {
+                                    float gap_length,
+                                    bool closed_path) {
   // Determine what number of dashes gives the minimum deviation from
-  // gapLength between dashes. Set the gap to that width.
-  float min_num_dashes =
-      floorf((stroke_length + gap_length) / (dash_length + gap_length));
+  // gap_length between dashes. Set the gap to that width.
+  float available_length =
+      closed_path ? stroke_length : stroke_length + gap_length;
+  float min_num_dashes = floorf(available_length / (dash_length + gap_length));
   float max_num_dashes = min_num_dashes + 1;
-  float min_gap =
-      (stroke_length - min_num_dashes * dash_length) / (min_num_dashes - 1);
-  float max_gap =
-      (stroke_length - max_num_dashes * dash_length) / (max_num_dashes - 1);
+  float min_num_gaps = closed_path ? min_num_dashes : min_num_dashes - 1;
+  float max_num_gaps = closed_path ? max_num_dashes : max_num_dashes - 1;
+  float min_gap = (stroke_length - min_num_dashes * dash_length) / min_num_gaps;
+  float max_gap = (stroke_length - max_num_dashes * dash_length) / max_num_gaps;
   return (max_gap <= 0) ||
                  (fabs(min_gap - gap_length) < fabs(max_gap - gap_length))
              ? min_gap
diff --git a/third_party/blink/renderer/platform/graphics/stroke_data.h b/third_party/blink/renderer/platform/graphics/stroke_data.h
index cf5e3b9..050789b 100644
--- a/third_party/blink/renderer/platform/graphics/stroke_data.h
+++ b/third_party/blink/renderer/platform/graphics/stroke_data.h
@@ -68,20 +68,20 @@
   void SetLineDash(const DashArray&, float);
 
   // Sets everything on the paint except the pattern, gradient and color.
+  void SetupPaint(cc::PaintFlags*) const;
+
+  // Setup any DashPathEffect on the paint.
   // If a non-zero length is provided, the number of dashes/dots on a
   // dashed/dotted line will be adjusted to start and end that length with a
   // dash/dot. If non-zero, dash_thickness is the thickness to use when
   // deciding on dash sizes. Used in border painting when we stroke thick
   // to allow for clipping at corners, but still want small dashes.
-  void SetupPaint(cc::PaintFlags*,
-                  const int length = 0,
-                  const int dash_thickess = 0) const;
-
-  // Setup any DashPathEffect on the paint. See SetupPaint above for parameter
-  // information.
+  // If closed_path is true, a gap will be allocated after the last dash, so
+  // that all dashes will be evenly spaced on the closed path.
   void SetupPaintDashPathEffect(cc::PaintFlags*,
                                 const int path_length = 0,
-                                const int dash_thickness = 0) const;
+                                const int dash_thickness = 0,
+                                const bool closed_path = false) const;
 
   // Determine whether a stroked line should be drawn using dashes. In practice,
   // we draw dashes when a dashed stroke is specified or when a dotted stroke
@@ -104,14 +104,15 @@
     return thickness >= 3 ? 1.0 : 2.0;
   }
 
+ private:
   // Return a dash gap size that places dashes at each end of a stroke that is
   // strokeLength long, given preferred dash and gap sizes. The gap returned is
   // the one that minimizes deviation from the preferred gap length.
   static float SelectBestDashGap(float stroke_length,
                                  float dash_length,
-                                 float gap_length);
+                                 float gap_length,
+                                 bool closed_path);
 
- private:
   StrokeStyle style_ = kSolidStroke;
   float thickness_ = 0;
   cc::PaintFlags::Cap line_cap_ = cc::PaintFlags::kDefault_Cap;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e5d8919e..08e293b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -687,6 +687,10 @@
       status: "experimental",
     },
     {
+      name: "CSSOverflowForReplacedElements",
+      status: "test",
+    },
+    {
       name: "CSSPaintAPIArguments",
       status: "experimental",
     },
diff --git a/third_party/blink/tools/blinkpy/tool/commands/command.py b/third_party/blink/tools/blinkpy/tool/commands/command.py
index f184d68..c765743 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/command.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/command.py
@@ -27,6 +27,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import optparse
+import os
 import logging
 import sys
 
@@ -148,3 +149,20 @@
         if self.epilog_method:
             return '\n%s\n' % self.epilog_method()
         return ''
+
+
+def check_file_option(option, _opt_str, value, parser):
+    if value:
+        value = os.path.expanduser(value)
+        if not os.path.isfile(value):
+            raise optparse.OptionValueError('%s is not a regular file.' %
+                                            value)
+    setattr(parser.values, option.dest, value)
+
+
+def check_dir_option(option, _opt_str, value, parser):
+    if value:
+        value = os.path.expanduser(value)
+        if not os.path.isdir(value):
+            raise optparse.OptionValueError('%s is not a directory.' % value)
+    setattr(parser.values, option.dest, value)
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
index b464773..521a5e8 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -37,7 +37,7 @@
 from blinkpy.common.path_finder import WEB_TESTS_LAST_COMPONENT
 from blinkpy.common.memoized import memoized
 from blinkpy.common.net.results_fetcher import Build
-from blinkpy.tool.commands.command import Command
+from blinkpy.tool.commands.command import Command, check_dir_option
 from blinkpy.web_tests.models import test_failures
 from blinkpy.web_tests.models.test_expectations import SystemConfigurationRemover, TestExpectations
 from blinkpy.web_tests.port import base, factory
@@ -68,7 +68,11 @@
          '(default is to de-dupe automatically). You can use "blink_tool.py '
          'optimize-baselines" to optimize separately.'))
     results_directory_option = optparse.make_option(
-        '--results-directory', help='Local results directory to use.')
+        '--results-directory',
+        action='callback',
+        callback=check_dir_option,
+        type='string',
+        help='Local results directory to use.')
     suffixes_option = optparse.make_option(
         '--suffixes',
         default=','.join(BASELINE_SUFFIX_LIST),
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
index 6b60bfb..ccc3b3c 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
@@ -12,6 +12,7 @@
 
 from blinkpy.common.net.git_cl import GitCL, TryJobStatus
 from blinkpy.common.path_finder import PathFinder
+from blinkpy.tool.commands.command import check_file_option
 from blinkpy.tool.commands.rebaseline import AbstractParallelRebaselineCommand
 from blinkpy.tool.commands.rebaseline import TestBaselineSet
 
@@ -72,7 +73,9 @@
             optparse.make_option(
                 '--test-name-file',
                 dest='test_name_file',
-                default=None,
+                action='callback',
+                callback=check_file_option,
+                type='string',
                 help='Read names of tests to rebaseline from this file, one '
                 'test per line.'),
             optparse.make_option(
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7dcb19b..6f64c12 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7070,3 +7070,8 @@
 crbug.com/1339293 [ Linux ] media/media-controls-overflow-hidden.html [ Failure Pass ]
 crbug.com/1339293 [ Linux ] virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/029.html [ Failure Pass ]
 crbug.com/1339293 [ Linux ] virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF-dynamic.html [ Failure Pass ]
+
+crbug.com/1339755 [ Mac10.13 ] compositing/overflow/nested-render-surfaces-with-rotation.html [ Skip ]
+crbug.com/1339755 [ Mac10.13 ] media/video-zoom-controls.html [ Skip ]
+crbug.com/1339755 [ Mac10.13 ] virtual/oopr-canvas2d/fast/canvas/image-object-in-canvas.html [ Skip ]
+crbug.com/1339755 [ Mac10.13 ] virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo.xml [ Skip ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 82f7ec5..e109761 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: e763fd7606e4c237247d6aaa53b61d3f87b692c4
+Version: 2958c3d093920c58f2b8e73de8c5b8e4cadbc158
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 91aa0ae..f4231330 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -180890,7 +180890,7 @@
       ]
      ],
      "text-decoration-subelements-002.html": [
-      "c3af8e62df553c10a27c0522fe619a56740eb729",
+      "8ac1afa705677262d847686a769df0a0a8a1de79",
       [
        null,
        [
@@ -180899,7 +180899,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            75,
+            75
+           ],
+           [
+            42,
+            42
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-subelements-003.html": [
@@ -180915,6 +180931,32 @@
        {}
       ]
      ],
+     "text-decoration-subelements-004.html": [
+      "9d23727e27dcbba6f6eebba91746220aad161eac",
+      [
+       null,
+       [
+        [
+         "/css/css-text-decor/reference/text-decoration-subelements-004-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-decoration-subelements-005.html": [
+      "1ed03d2aadf8855826ba3435c72b373c43c0843e",
+      [
+       null,
+       [
+        [
+         "/css/css-text-decor/reference/text-decoration-subelements-005-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "text-decoration-thickness-001.html": [
       "8f314793f2634406c8ad5d3fa268bf56eb864406",
       [
@@ -288183,6 +288225,14 @@
        "5e229a9b72f65cc3863232a489ffe8dea17c8760",
        []
       ],
+      "text-decoration-subelements-004-ref.html": [
+       "2f02494c40f6e4c733c4e0b7ab09d84af6156d41",
+       []
+      ],
+      "text-decoration-subelements-005-ref.html": [
+       "77f385a3c7b03ed5b8b47bc0f04541723d815ea4",
+       []
+      ],
       "text-decoration-thickness-001-notref.html": [
        "4e7db88ce5b41e28aa43fc339c35438855abb928",
        []
@@ -374994,7 +375044,7 @@
       ]
      ],
      "linear-timing-functions-output.tentative.html": [
-      "6817f535f1dca092986d7a144ba7acc592061fdd",
+      "132840357188adb30bb51c28d5f00b39bc0140c8",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html
index 75b80af..a19cf6a4 100644
--- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html
+++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html
@@ -15,39 +15,36 @@
     providers: [{
       url: url_prefix,
       clientId: '1',
+      nonce: '2',
     }]
   }
 };
-const login_options = {
-  nonce: '2',
-};
 
 promise_test(async t => {
   await set_fedcm_cookie();
   const cred = await navigator.credentials.get(test_options);
-  const token = await cred.login(login_options);
-  assert_equals(token.idToken, "token");
+  assert_equals(cred.idToken, "token");
 }, "Successfully obtaining id_token should resolve the promise.");
 
 promise_test(async t => {
   await set_fedcm_cookie();
-  const first = await navigator.credentials.get(test_options);
-  const second = await navigator.credentials.get(test_options);
-  const first_cred = first.login(login_options);
-  const second_cred = second.login(login_options);
+  const first = navigator.credentials.get(test_options);
+  const second = navigator.credentials.get(test_options);
+
   // We have to call promise_rejects_dom here, because if we call it after
   // the promise gets rejected, the unhandled rejection event handler is called
   // and fails the test even if we handle the rejection later.
-  const rej = promise_rejects_dom(t, 'AbortError', second_cred);
+  const rej = promise_rejects_dom(t, 'AbortError', second);
 
-  const first_token = await first_cred;
-  assert_equals(first_token.idToken, "token");
+  const first_cred = await first;
+  assert_equals(first_cred.idToken, "token");
+
   return rej;
 },
 "When there's a pending request, a second `get` call should be rejected. ");
 
 promise_test(async t => {
-  const result = navigator.credentials.get({
+  const cred = navigator.credentials.get({
   federated: {
     providers: [{
       clientId: '1',
@@ -55,11 +52,11 @@
     }]
   }
   });
-  return promise_rejects_js(t, TypeError, result);
+  return promise_rejects_js(t, TypeError, cred);
 }, "Reject when url is missing" );
 
 promise_test(async t => {
-  const result = navigator.credentials.get({
+  const cred = navigator.credentials.get({
   federated: {
     providers: [{
       url: 'test',
@@ -67,11 +64,11 @@
     }]
   }
   });
-  return promise_rejects_dom(t, "InvalidStateError", result);
+  return promise_rejects_dom(t, "InvalidStateError", cred);
 }, "Reject when url is invalid");
 
 promise_test(async t => {
-  const result = navigator.credentials.get({
+  const cred = navigator.credentials.get({
   federated: {
     providers: [{
       url: 'https://idp.test',
@@ -79,83 +76,85 @@
     }]
   }
   });
-  return promise_rejects_dom(t, "InvalidStateError", result);
+  return promise_rejects_dom(t, "InvalidStateError", cred);
 }, "Reject when clientId is empty");
 
 promise_test(async t => {
-  const cred = await navigator.credentials.get(test_options);
-  const token = await cred.login({});
-
-  assert_equals(token.idToken, "token");
+  const cred = await navigator.credentials.get({
+    federated: {
+      providers: [{
+        url: url_prefix,
+        clientId: '1',
+      }]
+    }
+  });
+  assert_equals(cred.idToken, "token");
 }, "nonce is not required in FederatedIdentityProvider.");
 
 promise_test(async t => {
-  const result = navigator.credentials.get({
+  const cred = navigator.credentials.get({
   federated: {
     providers: [{
       url: 'https://idp.test',
     }]
   }
   });
-  return promise_rejects_js(t, TypeError, result);
+  return promise_rejects_js(t, TypeError, cred);
 }, "Reject when clientId is missing" );
 
 promise_test(async t => {
   let controller = new AbortController();
-  const cred = await navigator.credentials.get(test_options);
-  const token_promise = cred.login({
-    nonce: '2',
+  const cred = navigator.credentials.get({
+    federated: test_options.federated,
     signal: controller.signal,
   });
   controller.abort();
-  return promise_rejects_dom(t, 'AbortError', token_promise);
-}, "test the abort signal");
+  return promise_rejects_dom(t, 'AbortError', cred);
+}, "Test the abort signal");
 
 promise_test(async t => {
   let controller = new AbortController();
-  const cred = await navigator.credentials.get(test_options);
-  const token_promise = cred.login({
-    nonce: '2',
+  const first_cred = navigator.credentials.get({
+    federated: test_options.federated,
     signal: controller.signal,
   });
   controller.abort();
-  await promise_rejects_dom(t, 'AbortError', token_promise);
+  await promise_rejects_dom(t, 'AbortError', first_cred);
 
-  const cred2 = await navigator.credentials.get(test_options);
-  const token = await cred2.login(login_options);
-  assert_equals(token.idToken, "token");
-}, "get after abort should work");
+  const second_cred = await navigator.credentials.get(test_options);
+  assert_equals(second_cred.idToken, "token");
+}, "Get after abort should work");
 
 promise_test(async t => {
-  const result = navigator.credentials.get({
-  federated: {
-    providers: [{
-      url: 'https://other-idp.test/',
-      clientId: '1',
-    }]
-  }
-  });
-  return promise_rejects_dom(t, "NetworkError", result);
-}, "Provider URL should honor Content-Security-Policy.");
-
-promise_test(async t => {
-  await set_fedcm_cookie();
-  const result = await navigator.credentials.get(test_options);
-  const token = await result.login(login_options);
-  assert_equals(token.idToken, 'token');
-}, 'Test that COEP policy do not apply to FedCM requests');
-
-promise_test(async t => {
-  await set_fedcm_cookie();
-  const result = await navigator.credentials.get({
+  const cred = navigator.credentials.get({
     federated: {
       providers: [{
-        url: url_prefix + "fedcm-manifest-not-in-list/",
+        url: 'https://other-idp.test/',
         clientId: '1',
       }]
     }
   });
-  return promise_rejects_dom(t, 'NetworkError', result.login(login_options));
+  return promise_rejects_dom(t, "NetworkError", cred);
+}, "Provider URL should honor Content-Security-Policy.");
+
+promise_test(async t => {
+  await set_fedcm_cookie();
+  const cred = await navigator.credentials.get(test_options);
+  assert_equals(cred.idToken, 'token');
+}, 'Test that COEP policy do not apply to FedCM requests');
+
+promise_test(async t => {
+  await set_fedcm_cookie();
+  const cred = navigator.credentials.get({
+    federated: {
+      providers: [{
+        url: url_prefix + "fedcm-manifest-not-in-list/",
+        clientId: '1',
+        nonce: '2',
+      }]
+    }
+  });
+  return promise_rejects_dom(t, 'NetworkError', cred);
 }, 'Test that the promise is rejected if the manifest is not in the manifest list');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html
deleted file mode 100644
index 1bc3ebb..0000000
--- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<title>Federated Credential Management API basics.</title>
-<link rel="help" href="https://wicg.github.io/FedCM">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script type="module">
-  import {fedcm_test} from './support/fedcm-helper.js';
-
-  const test_options = {
-    federated: {
-      providers: [{
-        url: 'https://idp.test',
-        clientId: '1',
-      }]
-    }
-  };
-  const login_options = {
-    nonce: '2',
-  };
-
-
-  fedcm_test(async (t, mock) => {
-    mock.returnError("ApprovalDeclined");
-    const cred = await navigator.credentials.get(test_options);
-    const token = cred.login(login_options);
-    return promise_rejects_dom(t, 'AbortError', token);
-  }, "User approval decline should reject the promise.");
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-iframe.html b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-iframe.html
index 0601971..546f66a 100644
--- a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-iframe.html
+++ b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-iframe.html
@@ -7,12 +7,10 @@
     providers: [{
       url: 'https://idp.test',
       clientId: '1',
+      nonce: '2',
     }]
   }
 };
-const login_options = {
-  nonce: '2',
-};
 
 // Loading fedcm-iframe.html in the test will make a FedCM call on load, and
 // trigger a postMessage upon completion.
@@ -28,8 +26,7 @@
     const mock = new MockFederatedAuthRequest();
     mock.returnIdToken("a_token");
     const cred = await navigator.credentials.get(test_options);
-    const token = await cred.login(login_options);
-    window.top.postMessage({result: "Pass", token: token.idToken}, '*');
+    window.top.postMessage({result: "Pass", token: cred.idToken}, '*');
   } catch (error) {
     window.top.postMessage({result: "Fail", errorType: error.name}, '*');
   }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-units-in-at-container-fallback.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-units-in-at-container-fallback.html
new file mode 100644
index 0000000..3784499c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-units-in-at-container-fallback.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Container Relative Units: container relative units fall back to small viewport</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+  iframe {
+    width: 200px;
+    height: 320px;
+  }
+</style>
+<iframe id=iframe srcdoc="
+  <style>
+    #parent {
+      container-type: inline-size;
+      width: 64px;
+      height: 50px;
+    }
+    #container {
+      container-type: size;
+      width: 32px;
+      height: 32px;
+    }
+
+    #target1, #target2 { color: green; }
+
+    /* Unit should evaluate against width of #parent */
+    @container ((height = 32px) and (height = 50cqw)) {
+      #target1 { color: blue; }
+    }
+
+    /* Unit should evaluate against height of iframe */
+    @container ((height = 32px) and (height = 10cqh)) {
+      #target2 { color: blue; }
+    }
+
+  </style>
+  <div id=parent>
+    <div id=container>
+      <div id=target1></div>
+      <div id=target2></div>
+    </div>
+  </div>
+"></iframe>
+<script>
+  setup(() => assert_implements_container_queries());
+
+  function waitForLoad(w) {
+    return new Promise(resolve => w.addEventListener('load', resolve));
+  }
+
+  promise_test(async () => {
+    await waitForLoad(window);
+    let inner_target1 = iframe.contentDocument.querySelector('#target1');
+    let inner_target2 = iframe.contentDocument.querySelector('#target2');
+    assert_equals(getComputedStyle(inner_target1).color, 'rgb(0, 0, 255)');
+    assert_equals(getComputedStyle(inner_target2).color, 'rgb(0, 0, 255)');
+
+    iframe.style = 'height:400px';
+
+    // #target1 is not affected since it evaluated against another container.
+    // #target2 *is* affected, because it evaluated against the iframe size
+    // which just changed.
+    assert_equals(getComputedStyle(inner_target1).color, 'rgb(0, 0, 255)');
+    assert_equals(getComputedStyle(inner_target2).color, 'rgb(0, 128, 0)');
+  }, 'Use small viewport size as fallback');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-units-in-at-container.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-units-in-at-container.html
new file mode 100644
index 0000000..9ddca55e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-units-in-at-container.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<title>Container Relative Units: in @container prelude</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+  .size { container-type: size; }
+  .inline { container-type: inline-size; }
+  .ancestor {
+     container-type: size;
+     width: 64px;
+     height: 160px;
+  }
+  .parent {
+     container-type: inline-size;
+     width: 32px;
+     height: 77px;
+   }
+  .container {
+    container-type: size;
+    width: 16px;
+    height: 16px;
+  }
+
+  /* Unit should resolve against .parent width. */
+  @container ((width = 16px) and (width = 50cqw)) { #child1 { --cqw:true;  } }
+
+  /* Unit should resolve against .ancestor height. */
+  @container ((width = 16px) and (width = 10cqh)) { #child1 { --cqh:true;  } }
+
+  /* Unit should resolve against .parent width. */
+  @container ((width = 16px) and (width = 50cqi)) { #child1 { --cqi:true;  } }
+
+  /* Unit should resolve against .ancestor height. */
+  @container ((width = 16px) and (width = 10cqb)) { #child1 { --cqb:true;  } }
+
+  /* Unit should resolve against biggest of w/h. */
+  @container ((width = 16px) and (width = 10cqmax)) { #child1 { --cqmax:true;  } }
+
+  /* Unit should resolve against smallest of w/h. */
+  @container ((width = 16px) and (width = 50cqmin)) { #child1 { --cqmin:true;  } }
+
+  /* Flipped writing mode: */
+
+  /* Non-logical units are the same as above */
+  @container ((width = 16px) and (width = 50cqw)) { #child2 { --cqw:true;  } }
+  @container ((width = 16px) and (width = 10cqh)) { #child2 { --cqh:true;  } }
+  @container ((width = 16px) and (width = 10cqmax)) { #child2 { --cqmax:true;  } }
+  @container ((width = 16px) and (width = 50cqmin)) { #child2 { --cqmin:true;  } }
+
+  /* Unit should resolve against .ancestor height. */
+  @container ((width = 16px) and (width = 50cqb)) { #child2 { --cqi:true;  } }
+
+  /* Unit should resolve against .parent width. */
+  @container ((width = 16px) and (width = 10cqi)) { #child2 { --cqb:true;  } }
+</style>
+
+<div class=ancestor>
+  <div class=parent>
+    <div class=container>
+      <div id=child1>Test1</div>
+    </div>
+  </div>
+</div>
+
+<div class=ancestor>
+  <div class=parent>
+    <div class=container style="writing-mode:vertical-rl;">
+      <div id=child2>Test1</div>
+    </div>
+  </div>
+</div>
+
+<script>
+  setup(() => assert_implements_container_queries());
+
+  let units = [
+    'cqw',
+    'cqh',
+    'cqi',
+    'cqb',
+    'cqmin',
+    'cqmax',
+  ];
+
+  for (let unit of units) {
+    test(() => {
+      assert_equals(getComputedStyle(child1).getPropertyValue(`--${unit}`), 'true');
+    }, `${unit} unit resolves against appropriate container`);
+  }
+
+  // Ensure that the writing mode of the subject element is not relevant for
+  // container-relative units in the @container prelude.
+  for (let unit of units) {
+    test((t) => {
+      t.add_cleanup(() => {
+        child1.style = '';
+      });
+      child1.style.writingMode = 'vertical-rl';
+      assert_equals(getComputedStyle(child1).getPropertyValue(`--${unit}`), 'true');
+    }, `${unit} unit resolves against appropriate container (vertical writing-mode on subject)`);
+  }
+
+  for (let unit of units) {
+    test(() => {
+      assert_equals(getComputedStyle(child2).getPropertyValue(`--${unit}`), 'true');
+    }, `${unit} unit resolves against appropriate container (vertical writing-mode on container)`);
+  }
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-output.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-output.tentative.html
index 6817f53..1328403 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-output.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-output.tentative.html
@@ -105,5 +105,26 @@
 
   assert_animations_equal_at(anim, equiv, 1000);
 }, 'linear function easing, steps equivalent');
+
+test(function(t) {
+  var anim = create_animated_div(t, 'linear(0, 0.1 -10%, 1)');
+  var equiv = create_animated_div(t, 'linear(0, 0.1 0%, 1)');
+
+  assert_animations_equal_at(anim, equiv, 0);
+  assert_animations_equal_at(anim, equiv, 100);
+  assert_animations_equal_at(anim, equiv, 550);
+  assert_animations_equal_at(anim, equiv, 1000);
+}, 'linear function easing, input value being unspecified in the first entry implies zero');
+
+test(function(t) {
+  var anim = create_animated_div(t, 'linear(0, 0.9 110%, 1)');
+  var equiv = create_animated_div(t, 'linear(0, 0.9 110%, 1 110%)');
+
+  assert_animations_equal_at(anim, equiv, 0);
+  assert_animations_equal_at(anim, equiv, 450);
+  assert_animations_equal_at(anim, equiv, 900);
+  assert_animations_equal_at(anim, equiv, 950);
+  assert_animations_equal_at(anim, equiv, 1000);
+}, 'linear function easing, input value being unspecified in the last entry implies max input value');
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-canvas-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-canvas-ref.html
new file mode 100644
index 0000000..3ad440e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-canvas-ref.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow applies to canvas elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
+<link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<script src="../css-images/support/testHelper.js"></script>
+<body>
+<canvas></canvas>
+</body>
+<script>
+  paintCanvases();
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-canvas.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-canvas.html
new file mode 100644
index 0000000..e9529cb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-canvas.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow applies to canvas elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
+<link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="overflow-canvas-ref.html">
+<script src="../css-images/support/testHelper.js"></script>
+<style>
+  .default {
+    width: 25px;
+    height: 50px;
+    object-fit: none;
+    object-position: 0% 0%;
+    overflow: visible;
+  }
+</style>
+<body>
+<canvas class=default></canvas>
+</body>
+<script>
+  paintCanvases();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-video-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-video-ref.html
new file mode 100644
index 0000000..4a8a83e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-video-ref.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Verifies overflow applies to video elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
+<link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  body {
+    background: grey;
+  }
+</style>
+<video autoplay>
+  <source src="/media/white.webm" type="video/webm">
+  <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script type="text/javascript">
+  document.getElementsByTagName('video')[0].addEventListener('play', (event) => {
+    takeScreenshot();
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-video.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-video.html
new file mode 100644
index 0000000..1721f87
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-video.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Verifies overflow applies to video elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
+<link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="overflow-video-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  body {
+    background: grey;
+  }
+  .default {
+    width: 100px;
+    height: 100px;
+    object-fit: none;
+    object-position: 0% 0%;
+    overflow: visible;
+  }
+</style>
+<video autoplay class="default">
+  <source src="/media/white.webm" type="video/webm">
+  <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script type="text/javascript">
+  document.getElementsByTagName('video')[0].addEventListener('play', (event) => {
+    takeScreenshot();
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/OWNERS b/third_party/blink/web_tests/external/wpt/event-timing/OWNERS
index 9e3a2546..7f44d1b 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/event-timing/OWNERS
@@ -1 +1 @@
-npm@chromium.org
+iclelland@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/document-timeline-animation-ref.html b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/document-timeline-animation-ref.html
new file mode 100644
index 0000000..d1ee52a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/document-timeline-animation-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Reference for document timeline animation</title>
+<style>
+  #notes {
+    position: absolute;
+    left: 0px;
+    top: 100px;
+  }
+  body {
+    background: white;
+  }
+</style>
+<body>
+  <div id="box"></div>
+  <p id="notes">
+    This test creates a document timeline animation. If any blue pixels appear
+    in the screenshot, the test fails.
+  </p>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/document-timeline-animation.html b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/document-timeline-animation.html
new file mode 100644
index 0000000..5fbfd2ac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animations/document-timeline-animation.html
@@ -0,0 +1,61 @@
+
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="UTF-8">
+<title>document timeline animation</title>
+<link rel="match" href="document-timeline-animation-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="../../testcommon.js"></script>
+<style>
+  #box-1, #box-2 {
+    position: absolute;
+    top: 0px;
+    width: 40px;
+    height: 40px;
+  }
+  #box-1 {
+    background: blue;
+    z-index: 1;
+    left: 0px;
+  }
+  #box-2 {
+    background: white;
+    z-index: 2;
+    left: 100px;
+  }
+  #notes {
+    position: absolute;
+    left: 0px;
+    top: 100px;
+  }
+  body {
+    background: white;
+  }
+</style>
+
+<body>
+  <div id="box-1"></div>
+  <div id="box-2"></div>
+  <p id="notes">
+    This test creates a document timeline animation. If any blue pixels appear
+    in the screenshot, the test fails.
+  </p>
+</body>
+<script>
+  onload = async function() {
+    const elem = document.getElementById('box-1');
+    const keyframes = [
+      { transform: 'none' },
+      { transform: 'translateX(100px)' }
+    ];
+    const effect =
+        new KeyframeEffect(elem, keyframes,
+                           {iterations: 1, duration: 500, fill: 'forwards'});
+    const timeline = new DocumentTimeline();
+    const animation = new Animation(effect, timeline);
+    animation.play();
+    await animation.finished;
+    await waitForAnimationFrames(2);
+    takeScreenshot();
+  };
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html b/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html
index 7f0d3cd..cb0b581 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html
@@ -76,14 +76,8 @@
     });
     preferCodec(transceiver, 'video/H264');
 
-    let metadataLoaded = new Promise((resolve) => {
-      v.autoplay = true;
-      v.srcObject = stream;
-      v.id = stream.id
-      v.addEventListener('loadedmetadata', () => {
-        resolve();
-      });
-    });
+    exchangeIceCandidates(pc1, pc2);
+    const trackEvent = new Promise(r => pc2.ontrack = r);
 
     const offer = await pc1.createOffer();
     await pc1.setLocalDescription(offer),
@@ -91,6 +85,15 @@
     const answer = await pc2.createAnswer();
     await pc2.setLocalDescription(answer);
     await pc1.setRemoteDescription(mungeLevel(answer, level));
+
+    v.srcObject = new MediaStream([(await trackEvent).track]);
+    let metadataLoaded = new Promise((resolve) => {
+      v.autoplay = true;
+      v.id = stream.id
+      v.addEventListener('loadedmetadata', () => {
+        resolve();
+      });
+    });
     await metadataLoaded;
     // Ensure that H.264 is in fact used.
     const statsReport = await transceiver.sender.getStats();
@@ -102,6 +105,9 @@
                           'Level ' + level + ' H264 video is not supported');
       }
     }
+    // TODO(hta): This will not catch situations where the initial size is
+    // within the permitted bounds, but resolution or framerate changes to
+    // outside the permitted bounds after a while. Should be addressed.
     sizeFitsLevel(v.videoWidth, v.videoHeight, framesPerSecond, level);
   }, 'Level ' + level + ' H264 video is appropriately constrained');
 
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/border-radius-styles-with-composited-child-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/border-radius-styles-with-composited-child-expected.png
index 93d077fd..3e36319 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/border-radius-styles-with-composited-child-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/border-radius-styles-with-composited-child-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js
index 64cd288..f9dd720 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js
@@ -16,6 +16,11 @@
   });
 
   async function onFrameNavigated(event) {
+    if (event.params.frame.unreachableUrl) {
+      // Retry navigation in case the URL couldn't load
+      session.navigate(event.params.frame.unreachableUrl);
+      return;
+    }
     const frameId = event.params.frame.id;
     const {result} = await session.protocol.Network.getSecurityIsolationStatus({frameId});
     results.set(event.params.frame.url, {coep: result.status.coep, coop: result.status.coop})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js
index 08246b5..406929fe 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js
@@ -2,6 +2,11 @@
   const {page, session, dp} = await testRunner.startBlank(
     `Tests that the initiator position is correct even when that initiator is minified.`);
 
+  window.onerror = (msg) => testRunner.log('onerror: ' + msg);
+  window.onunhandledrejection = (e) => testRunner.log('onunhandledrejection: ' + e.reason);
+  let errorForLog = new Error();
+  setTimeout(() => testRunner.die('Timeout', errorForLog), 5000);
+
   dp.Network.enable();
   dp.Page.enable();
   dp.Page.navigate({url: testRunner.url('resources/minified.html')});
@@ -9,6 +14,7 @@
   let requests = [];
   await dp.Network.onceRequestWillBeSent(e => {
     requests.push(e.params);
+    errorForLog = new Error(JSON.stringify(requests));
     // Wait for all expected requests to be done.
     return requests.length === 4;
   });
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-origin-trial-interfaces.html
index 243718d8..35e3c8f 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-origin-trial-interfaces.html
@@ -13,7 +13,7 @@
     test(t => {
       OriginTrialsHelper.check_properties_exist(this,
         {
-          'FederatedCredential': ['login'],
+          'FederatedCredential': ['idToken'],
         });
   }, 'FedCM API interfaces and properties in Origin-Trial enabled document.');
 </script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-third-party-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-third-party-origin-trial-interfaces.html
index 288ebf5..99ec045e 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-third-party-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/fedcm-third-party-origin-trial-interfaces.html
@@ -9,7 +9,7 @@
     test(t => {
       OriginTrialsHelper.check_properties_exist(this,
         {
-          'FederatedCredential': ['login'],
+          'FederatedCredential': ['idToken'],
         });
   }, 'FedCM API interfaces and properties in Origin-Trial enabled document.');
 </script>
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-set-scope-text.js b/third_party/blink/web_tests/inspector-protocol/css/css-set-scope-text.js
new file mode 100644
index 0000000..761fea0
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-set-scope-text.js
@@ -0,0 +1,78 @@
+(async function(testRunner) {
+  const {dp} = await testRunner.startHTML(`
+<link rel='stylesheet' href='${testRunner.url('resources/set-scope-text.css')}'/>`, 'Tests CSS.setScopeText method.');
+
+  const CSSHelper = await testRunner.loadScript('../resources/css-helper.js');
+  const cssHelper = new CSSHelper(testRunner, dp);
+
+  dp.DOM.enable();
+  dp.CSS.enable();
+
+  const event = await dp.CSS.onceStyleSheetAdded();
+  const styleSheetId = event.params.header.styleSheetId;
+  const setScopeText = cssHelper.setScopeText.bind(cssHelper, styleSheetId, false);
+  const verifyProtocolError = cssHelper.setScopeText.bind(cssHelper, styleSheetId, true);
+
+  let response = await dp.CSS.getStyleSheetText({styleSheetId});
+  testRunner.log('==== Initial style sheet text ====');
+  testRunner.log(response.result.text);
+
+  const scopeRange = {
+    startLine: 0,
+    startColumn: 7,
+    endLine: 0,
+    endColumn: 38,
+  };
+
+  testRunner.runTestSuite([
+    async function testSimpleEdit() {
+      await setScopeText({
+        range: scopeRange,
+        text: '(body)',
+      });
+      await dp.DOM.undo();
+    },
+
+    async function testInvalidParameters() {
+      await verifyProtocolError({
+        range: { startLine: 'three', startColumn: 0, endLine: 4, endColumn: 0 },
+        text: 'no matter what is here',
+      });
+    },
+
+    async function testInvalidText() {
+      await verifyProtocolError({
+        range: scopeRange,
+        text: 'something { is { wrong: here} }'
+      });
+    },
+
+    async function testEditSequentially() {
+      const newText = '(body)';
+      const oldLength = scopeRange.endColumn - scopeRange.startColumn;
+      const lengthDelta = newText.length - oldLength;
+      await setScopeText({
+        range: scopeRange,
+        text: newText,
+      });
+
+      const newRange = {
+        ...scopeRange,
+        endColumn: scopeRange.endColumn + lengthDelta,
+      };
+      await setScopeText({
+        range: newRange,
+        text: '(.dark-theme)'
+      });
+      await dp.DOM.undo();
+    },
+
+    async function testAfterSequentially() {
+      await setScopeText({
+        range: scopeRange,
+        text: '(.light-theme) to (.dark-theme)'
+      });
+      await dp.DOM.undo();
+    },
+  ]);
+})
diff --git a/third_party/blink/web_tests/inspector-protocol/css/resources/set-scope-text.css b/third_party/blink/web_tests/inspector-protocol/css/resources/set-scope-text.css
new file mode 100644
index 0000000..f170997
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/css/resources/set-scope-text.css
@@ -0,0 +1,5 @@
+@scope (.dark-theme) to (.light-theme) {
+  p {
+    color: red;
+  }
+}
diff --git a/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js b/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js
index 9e44668e..19340a3 100644
--- a/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js
+++ b/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js
@@ -56,6 +56,12 @@
     await this._logMessage(message, expectError, styleSheetId);
   }
 
+  async setScopeText(styleSheetId, expectError, options) {
+    options.styleSheetId = styleSheetId;
+    var message = await this._dp.CSS.setScopeText(options);
+    await this._logMessage(message, expectError, styleSheetId);
+  }
+
   async addRule(styleSheetId, expectError, options) {
     options.styleSheetId = styleSheetId;
     var message = await this._dp.CSS.addRule(options);
diff --git a/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/outline/focus-layers-expected.png b/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/outline/focus-layers-expected.png
index ca92d13..e88f5fed 100644
--- a/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/outline/focus-layers-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/paint/invalidation/outline/focus-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png
index 5c14728..ea1ed59 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png
index 57ff916..4b16939e 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/compositing/overflow/border-radius-styles-with-composited-child-expected.png b/third_party/blink/web_tests/platform/generic/compositing/overflow/border-radius-styles-with-composited-child-expected.png
index 842cefa4..6acc9d74 100644
--- a/third_party/blink/web_tests/platform/generic/compositing/overflow/border-radius-styles-with-composited-child-expected.png
+++ b/third_party/blink/web_tests/platform/generic/compositing/overflow/border-radius-styles-with-composited-child-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/css3/filters/backdrop-filter-clip-radius-zoom-expected.png b/third_party/blink/web_tests/platform/generic/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
index 6e558e6..d8ec191 100644
--- a/third_party/blink/web_tests/platform/generic/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/generic/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
index 0a5fb59..83d8b6e 100644
--- a/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
@@ -1,19 +1,19 @@
 This is a testharness.js-based test.
 NOTRUN Level 1 H264 video is appropriately constrained Level 1 H264 video is not supported
-FAIL Level 2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 396 but got 32400
-FAIL Level 3 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 1620 but got 32400
-FAIL Level 4 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 8192 but got 32400
-FAIL Level 5 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 22800 but got 32400
+FAIL Level 2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 396 but got 2025
+FAIL Level 3 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 1620 but got 2025
+PASS Level 4 H264 video is appropriately constrained
+PASS Level 5 H264 video is appropriately constrained
 NOTRUN Level 6 H264 video is appropriately constrained Level 6 H264 video is not supported
 NOTRUN Level 1.1 H264 video is appropriately constrained Level 1.1 H264 video is not supported
 NOTRUN Level 1.2 H264 video is appropriately constrained Level 1.2 H264 video is not supported
 NOTRUN Level 1.3 H264 video is appropriately constrained Level 1.3 H264 video is not supported
-FAIL Level 2.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 792 but got 32400
-FAIL Level 2.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 1620 but got 32400
-FAIL Level 3.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 3600 but got 32400
-FAIL Level 3.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 5120 but got 32400
-FAIL Level 4.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 8192 but got 32400
-FAIL Level 4.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 8704 but got 32400
+FAIL Level 2.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 792 but got 2025
+FAIL Level 2.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 1620 but got 2025
+PASS Level 3.1 H264 video is appropriately constrained
+PASS Level 3.2 H264 video is appropriately constrained
+PASS Level 4.1 H264 video is appropriately constrained
+PASS Level 4.2 H264 video is appropriately constrained
 PASS Level 5.1 H264 video is appropriately constrained
 PASS Level 5.2 H264 video is appropriately constrained
 NOTRUN Level 6.1 H264 video is appropriately constrained Level 6.1 H264 video is not supported
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed01-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed01-expected.png
index 8786afe..1f95d07 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed01-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed01-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed02-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed02-expected.png
index ada337f..3cd1a46 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed02-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed02-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed03-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed03-expected.png
index 4065e0f..e016e811 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed03-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed03-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed04-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed04-expected.png
index 5008f76b..0cf22f38 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed04-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed04-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed05-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed05-expected.png
index 9efe71e..24007f7 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed05-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed05-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed06-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed06-expected.png
index ebb69c7..261718b 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed06-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDashed06-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted01-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted01-expected.png
index 5fef672..63402c8 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted01-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted01-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted02-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted02-expected.png
index 7a5509a..40c9a92 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted02-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted02-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted03-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted03-expected.png
index f1850805..f35b1787 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted03-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted03-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted04-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted04-expected.png
index 167098e8e..bd5cac72 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted04-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted04-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted06-expected.png b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted06-expected.png
index f4d0513..427a4cfe 100644
--- a/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted06-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/borders/borderRadiusDotted06-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-big-neg-offset-expected.png b/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-big-neg-offset-expected.png
index dd9758d..8c94a00 100644
--- a/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-big-neg-offset-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-big-neg-offset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-dashed-expected.png b/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-dashed-expected.png
index b2889675..91294a6 100644
--- a/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-dashed-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/css/inline-outline-with-border-radius-dashed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/fast/css/outline-with-border-radius-expected.png b/third_party/blink/web_tests/platform/generic/fast/css/outline-with-border-radius-expected.png
index 90bc9e7..a031a16a 100644
--- a/third_party/blink/web_tests/platform/generic/fast/css/outline-with-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/generic/fast/css/outline-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/fenced-frame/fenced-frame-auto-attach.https-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/fenced-frame/fenced-frame-auto-attach.https-expected.txt
index 9dd9a00..3c41419 100644
--- a/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/fenced-frame/fenced-frame-auto-attach.https-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/fenced-frame/fenced-frame-auto-attach.https-expected.txt
@@ -1,6 +1,6 @@
 Tests that target for fenced frame is auto attached and target info fields are correct
 Fenced frame target info: 
-type: page
+type: iframe
 url: https://127.0.0.1:8443/inspector-protocol/fenced-frame/resources/page-with-title.php
 title: A Title
 openerId: undefined
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-set-scope-text-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-set-scope-text-expected.txt
new file mode 100644
index 0000000..50c23d0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-set-scope-text-expected.txt
@@ -0,0 +1,49 @@
+Tests CSS.setScopeText method.
+==== Initial style sheet text ====
+@scope (.dark-theme) to (.light-theme) {
+  p {
+    color: red;
+  }
+}
+
+
+Running test: testSimpleEdit
+==== Style sheet text ====
+@scope (body) {
+  p {
+    color: red;
+  }
+}
+
+
+Running test: testInvalidParameters
+Expected protocol error: Invalid parameters (Failed to deserialize params.range.startLine - BINDINGS: int32 value expected <somewhere>)
+
+Running test: testInvalidText
+Expected protocol error: SyntaxError Selector or scope rule text is not valid.
+
+Running test: testEditSequentially
+==== Style sheet text ====
+@scope (body) {
+  p {
+    color: red;
+  }
+}
+
+==== Style sheet text ====
+@scope (.dark-theme) {
+  p {
+    color: red;
+  }
+}
+
+
+Running test: testAfterSequentially
+==== Style sheet text ====
+@scope (.light-theme) to (.dark-theme) {
+  p {
+    color: red;
+  }
+}
+
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png b/third_party/blink/web_tests/platform/generic/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
index 74be4cb..15ff585 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/generic/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
index 5ae6dcc..4d01ed2 100644
--- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
@@ -2623,6 +2623,7 @@
     static method logoutRps
     attribute @@toStringTag
     getter iconURL
+    getter idToken
     getter name
     getter protocol
     getter provider
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
similarity index 67%
rename from third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
rename to third_party/blink/web_tests/platform/linux/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
index 5d158ffe..88f2248 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
@@ -4,5 +4,7 @@
 FAIL linear function easing, linear equivalent Failed to execute 'animate' on 'Element': 'linear()' is not a valid value for easing
 FAIL linear function easing, constant Failed to execute 'animate' on 'Element': 'linear(0.5)' is not a valid value for easing
 FAIL linear function easing, steps equivalent Failed to execute 'animate' on 'Element': 'linear(0.2 0% 20%, 0.4 20% 40%, 0.6 40% 60%, 0.8 60% 80%, 1.0 80% 100%)' is not a valid value for easing
+FAIL linear function easing, input value being unspecified in the first entry implies zero Failed to execute 'animate' on 'Element': 'linear(0, 0.1 -10%, 1)' is not a valid value for easing
+FAIL linear function easing, input value being unspecified in the last entry implies max input value Failed to execute 'animate' on 'Element': 'linear(0, 0.9 110%, 1)' is not a valid value for easing
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/linux/fast/borders/borderRadiusAllStylesAllCorners-expected.png b/third_party/blink/web_tests/platform/linux/fast/borders/borderRadiusAllStylesAllCorners-expected.png
index 83a6dae..ea2dada0 100644
--- a/third_party/blink/web_tests/platform/linux/fast/borders/borderRadiusAllStylesAllCorners-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/borders/borderRadiusAllStylesAllCorners-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/borders/dashed-1px-with-border-radius-expected.png b/third_party/blink/web_tests/platform/linux/fast/borders/dashed-1px-with-border-radius-expected.png
index c042d0a..48c6592 100644
--- a/third_party/blink/web_tests/platform/linux/fast/borders/dashed-1px-with-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/borders/dashed-1px-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-expected.png b/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-expected.png
index e778712..54c4e1b 100644
--- a/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-subpixel-expected.png b/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-subpixel-expected.png
index 888abda..c064e30 100644
--- a/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-subpixel-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/box-shadow/inset-subpixel-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png b/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
index f96efcb..8580b6ec 100644
--- a/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
+++ b/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png b/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
index 1c463bc62..07ed3732 100644
--- a/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
+++ b/third_party/blink/web_tests/platform/linux/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-expected.png
index aff1c0c..a0785a2 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-subpixel-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-subpixel-expected.png
index e852a4d8..4b3f8ead7 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-subpixel-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/box-shadow/inset-subpixel-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-expected.png
index aff1c0c..a0785a2 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-subpixel-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-subpixel-expected.png
index e852a4d8..4b3f8ead7 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-subpixel-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/box-shadow/inset-subpixel-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
similarity index 67%
copy from third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
copy to third_party/blink/web_tests/platform/mac/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
index 5d158ffe..88f2248 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
@@ -4,5 +4,7 @@
 FAIL linear function easing, linear equivalent Failed to execute 'animate' on 'Element': 'linear()' is not a valid value for easing
 FAIL linear function easing, constant Failed to execute 'animate' on 'Element': 'linear(0.5)' is not a valid value for easing
 FAIL linear function easing, steps equivalent Failed to execute 'animate' on 'Element': 'linear(0.2 0% 20%, 0.4 20% 40%, 0.6 40% 60%, 0.8 60% 80%, 1.0 80% 100%)' is not a valid value for easing
+FAIL linear function easing, input value being unspecified in the first entry implies zero Failed to execute 'animate' on 'Element': 'linear(0, 0.1 -10%, 1)' is not a valid value for easing
+FAIL linear function easing, input value being unspecified in the last entry implies max input value Failed to execute 'animate' on 'Element': 'linear(0, 0.9 110%, 1)' is not a valid value for easing
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/fast/borders/borderRadiusAllStylesAllCorners-expected.png b/third_party/blink/web_tests/platform/mac/fast/borders/borderRadiusAllStylesAllCorners-expected.png
index 0f4a75e..51143dd1 100644
--- a/third_party/blink/web_tests/platform/mac/fast/borders/borderRadiusAllStylesAllCorners-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/borders/borderRadiusAllStylesAllCorners-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/borders/dashed-1px-with-border-radius-expected.png b/third_party/blink/web_tests/platform/mac/fast/borders/dashed-1px-with-border-radius-expected.png
index 617050db..385b5961 100644
--- a/third_party/blink/web_tests/platform/mac/fast/borders/dashed-1px-with-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/borders/dashed-1px-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/borders/outline-rounded-border-expected.png b/third_party/blink/web_tests/platform/mac/fast/borders/outline-rounded-border-expected.png
index 1f690fa..50c2d899 100644
--- a/third_party/blink/web_tests/platform/mac/fast/borders/outline-rounded-border-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/borders/outline-rounded-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-expected.png b/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-expected.png
index 0ebf46e..851562d 100644
--- a/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-subpixel-expected.png b/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-subpixel-expected.png
index 55783099..35df7d3 100644
--- a/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-subpixel-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/box-shadow/inset-subpixel-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/outline-with-border-radius-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/outline-with-border-radius-expected.png
index 174a9b8..b3cff75 100644
--- a/third_party/blink/web_tests/platform/mac/fast/css/outline-with-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/css/outline-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png b/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
index 12a32fab..86bdde3 100644
--- a/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png b/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
index 6814f01..b9168dc 100644
--- a/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
+++ b/third_party/blink/web_tests/platform/mac/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/borders/borderRadiusAllStylesAllCorners-expected.png b/third_party/blink/web_tests/platform/win/fast/borders/borderRadiusAllStylesAllCorners-expected.png
index f66fd15..2384f7a 100644
--- a/third_party/blink/web_tests/platform/win/fast/borders/borderRadiusAllStylesAllCorners-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/borders/borderRadiusAllStylesAllCorners-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/borders/dashed-1px-with-border-radius-expected.png b/third_party/blink/web_tests/platform/win/fast/borders/dashed-1px-with-border-radius-expected.png
index 62dc2bd18..3a49aa1 100644
--- a/third_party/blink/web_tests/platform/win/fast/borders/dashed-1px-with-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/borders/dashed-1px-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/borders/outline-rounded-border-expected.png b/third_party/blink/web_tests/platform/win/fast/borders/outline-rounded-border-expected.png
index 69e2940..3b30c1ad 100644
--- a/third_party/blink/web_tests/platform/win/fast/borders/outline-rounded-border-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/borders/outline-rounded-border-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-expected.png b/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-expected.png
index 9252b56c..20d38a0b 100644
--- a/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-subpixel-expected.png b/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-subpixel-expected.png
index 0ef0177..e3919291 100644
--- a/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-subpixel-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/box-shadow/inset-subpixel-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/select-popup/popup-menu-appearance-custom-scrollbar-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/select-popup/popup-menu-appearance-custom-scrollbar-expected.png
index 58139c4..dcadc1cd 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/select-popup/popup-menu-appearance-custom-scrollbar-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/select-popup/popup-menu-appearance-custom-scrollbar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png b/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
index ffd46e6..108e012 100644
--- a/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
+++ b/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png b/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
index 299db45..d510583 100644
--- a/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
+++ b/third_party/blink/web_tests/platform/win/ietestcenter/css3/bordersbackgrounds/border-radius-style-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
similarity index 67%
copy from third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
copy to third_party/blink/web_tests/platform/win10/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
index 5d158ffe..88f2248 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
+++ b/third_party/blink/web_tests/platform/win10/external/wpt/css/css-easing/linear-timing-functions-output.tentative-expected.txt
@@ -4,5 +4,7 @@
 FAIL linear function easing, linear equivalent Failed to execute 'animate' on 'Element': 'linear()' is not a valid value for easing
 FAIL linear function easing, constant Failed to execute 'animate' on 'Element': 'linear(0.5)' is not a valid value for easing
 FAIL linear function easing, steps equivalent Failed to execute 'animate' on 'Element': 'linear(0.2 0% 20%, 0.4 20% 40%, 0.6 40% 60%, 0.8 60% 80%, 1.0 80% 100%)' is not a valid value for easing
+FAIL linear function easing, input value being unspecified in the first entry implies zero Failed to execute 'animate' on 'Element': 'linear(0, 0.1 -10%, 1)' is not a valid value for easing
+FAIL linear function easing, input value being unspecified in the last entry implies max input value Failed to execute 'animate' on 'Element': 'linear(0, 0.9 110%, 1)' is not a valid value for easing
 Harness: the test ran to completion.
 
diff --git a/third_party/grpc/BUILD.gn b/third_party/grpc/BUILD.gn
index 8d33f84..a8ec01ee 100644
--- a/third_party/grpc/BUILD.gn
+++ b/third_party/grpc/BUILD.gn
@@ -269,6 +269,7 @@
     "src/include/grpcpp/create_channel.h",
     "src/include/grpcpp/create_channel_binder.h",
     "src/include/grpcpp/create_channel_posix.h",
+    "src/include/grpcpp/ext/call_metric_recorder.h",
     "src/include/grpcpp/ext/health_check_service_server_builder_option.h",
     "src/include/grpcpp/generic/async_generic_service.h",
     "src/include/grpcpp/generic/generic_stub.h",
@@ -390,6 +391,7 @@
     "src/src/core/ext/filters/client_channel/http_proxy.h",
     "src/src/core/ext/filters/client_channel/lb_policy.h",
     "src/src/core/ext/filters/client_channel/lb_policy/address_filtering.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/backend_metric_data.h",
     "src/src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h",
     "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.h",
     "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric.h",
@@ -540,6 +542,8 @@
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/rbac/v3/rbac.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/secret.upb.h",
@@ -755,6 +759,7 @@
     "src/src/core/lib/channel/call_tracer.h",
     "src/src/core/lib/channel/channel_args.h",
     "src/src/core/lib/channel/channel_args_preconditioning.h",
+    "src/src/core/lib/channel/channel_fwd.h",
     "src/src/core/lib/channel/channel_stack.h",
     "src/src/core/lib/channel/channel_stack_builder.h",
     "src/src/core/lib/channel/channel_stack_builder_impl.h",
@@ -773,7 +778,9 @@
     "src/src/core/lib/debug/trace.h",
     "src/src/core/lib/event_engine/channel_args_endpoint_config.h",
     "src/src/core/lib/event_engine/event_engine_factory.h",
-    "src/src/core/lib/event_engine/sockaddr.h",
+    "src/src/core/lib/event_engine/handle_containers.h",
+    "src/src/core/lib/event_engine/iomgr_engine.h",
+    "src/src/core/lib/event_engine/trace.h",
     "src/src/core/lib/gpr/alloc.h",
     "src/src/core/lib/gpr/env.h",
     "src/src/core/lib/gpr/murmur_hash.h",
@@ -786,7 +793,6 @@
     "src/src/core/lib/gpr/useful.h",
     "src/src/core/lib/gprpp/atomic_utils.h",
     "src/src/core/lib/gprpp/bitset.h",
-    "src/src/core/lib/gprpp/capture.h",
     "src/src/core/lib/gprpp/chunked_vector.h",
     "src/src/core/lib/gprpp/construct_destruct.h",
     "src/src/core/lib/gprpp/cpp_impl_of.h",
@@ -815,6 +821,7 @@
     "src/src/core/lib/gprpp/thd.h",
     "src/src/core/lib/gprpp/time.h",
     "src/src/core/lib/gprpp/time_util.h",
+    "src/src/core/lib/gprpp/unique_type_name.h",
     "src/src/core/lib/http/format_request.h",
     "src/src/core/lib/http/httpcli.h",
     "src/src/core/lib/http/httpcli_ssl_credentials.h",
@@ -836,12 +843,6 @@
     "src/src/core/lib/iomgr/ev_epoll1_linux.h",
     "src/src/core/lib/iomgr/ev_poll_posix.h",
     "src/src/core/lib/iomgr/ev_posix.h",
-    "src/src/core/lib/iomgr/event_engine/closure.h",
-    "src/src/core/lib/iomgr/event_engine/endpoint.h",
-    "src/src/core/lib/iomgr/event_engine/pollset.h",
-    "src/src/core/lib/iomgr/event_engine/promise.h",
-    "src/src/core/lib/iomgr/event_engine/resolved_address_internal.h",
-    "src/src/core/lib/iomgr/event_engine/resolver.h",
     "src/src/core/lib/iomgr/exec_ctx.h",
     "src/src/core/lib/iomgr/executor.h",
     "src/src/core/lib/iomgr/executor/mpmcqueue.h",
@@ -971,7 +972,7 @@
     "src/src/core/lib/security/security_connector/fake/fake_security_connector.h",
     "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.h",
     "src/src/core/lib/security/security_connector/load_system_roots.h",
-    "src/src/core/lib/security/security_connector/load_system_roots_linux.h",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.h",
     "src/src/core/lib/security/security_connector/local/local_security_connector.h",
     "src/src/core/lib/security/security_connector/security_connector.h",
     "src/src/core/lib/security/security_connector/ssl/ssl_security_connector.h",
@@ -1026,6 +1027,7 @@
     "src/src/core/lib/transport/tcp_connect_handshaker.h",
     "src/src/core/lib/transport/timeout_encoding.h",
     "src/src/core/lib/transport/transport.h",
+    "src/src/core/lib/transport/transport_fwd.h",
     "src/src/core/lib/transport/transport_impl.h",
     "src/src/core/lib/uri/uri_parser.h",
     "src/src/core/tsi/alts/crypt/gsec.h",
@@ -1122,11 +1124,9 @@
     "src/src/core/ext/filters/client_channel/proxy_mapper_registry.cc",
     "src/src/core/ext/filters/client_channel/resolver/binder/binder_resolver.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
-    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
-    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_event_engine.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc",
@@ -1268,6 +1268,8 @@
     "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/secret.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/tls.upb.c",
@@ -1466,11 +1468,11 @@
     "src/src/core/lib/event_engine/channel_args_endpoint_config.cc",
     "src/src/core/lib/event_engine/default_event_engine_factory.cc",
     "src/src/core/lib/event_engine/event_engine.cc",
+    "src/src/core/lib/event_engine/iomgr_engine.cc",
     "src/src/core/lib/event_engine/memory_allocator.cc",
     "src/src/core/lib/event_engine/resolved_address.cc",
     "src/src/core/lib/event_engine/slice.cc",
     "src/src/core/lib/event_engine/slice_buffer.cc",
-    "src/src/core/lib/event_engine/sockaddr.cc",
     "src/src/core/lib/gpr/alloc.cc",
     "src/src/core/lib/gpr/atm.cc",
     "src/src/core/lib/gpr/cpu_iphone.cc",
@@ -1528,7 +1530,6 @@
     "src/src/core/lib/iomgr/dualstack_socket_posix.cc",
     "src/src/core/lib/iomgr/endpoint.cc",
     "src/src/core/lib/iomgr/endpoint_cfstream.cc",
-    "src/src/core/lib/iomgr/endpoint_pair_event_engine.cc",
     "src/src/core/lib/iomgr/endpoint_pair_posix.cc",
     "src/src/core/lib/iomgr/endpoint_pair_windows.cc",
     "src/src/core/lib/iomgr/error.cc",
@@ -1538,13 +1539,6 @@
     "src/src/core/lib/iomgr/ev_poll_posix.cc",
     "src/src/core/lib/iomgr/ev_posix.cc",
     "src/src/core/lib/iomgr/ev_windows.cc",
-    "src/src/core/lib/iomgr/event_engine/closure.cc",
-    "src/src/core/lib/iomgr/event_engine/iomgr.cc",
-    "src/src/core/lib/iomgr/event_engine/pollset.cc",
-    "src/src/core/lib/iomgr/event_engine/resolved_address_internal.cc",
-    "src/src/core/lib/iomgr/event_engine/resolver.cc",
-    "src/src/core/lib/iomgr/event_engine/tcp.cc",
-    "src/src/core/lib/iomgr/event_engine/timer.cc",
     "src/src/core/lib/iomgr/exec_ctx.cc",
     "src/src/core/lib/iomgr/executor.cc",
     "src/src/core/lib/iomgr/executor/mpmcqueue.cc",
@@ -1558,6 +1552,7 @@
     "src/src/core/lib/iomgr/grpc_if_nametoindex_unsupported.cc",
     "src/src/core/lib/iomgr/internal_errqueue.cc",
     "src/src/core/lib/iomgr/iocp_windows.cc",
+    "src/src/core/lib/iomgr/iomgr.cc",
     "src/src/core/lib/iomgr/iomgr_internal.cc",
     "src/src/core/lib/iomgr/iomgr_posix.cc",
     "src/src/core/lib/iomgr/iomgr_posix_cfstream.cc",
@@ -1565,6 +1560,7 @@
     "src/src/core/lib/iomgr/load_file.cc",
     "src/src/core/lib/iomgr/lockfree_event.cc",
     "src/src/core/lib/iomgr/polling_entity.cc",
+    "src/src/core/lib/iomgr/pollset.cc",
     "src/src/core/lib/iomgr/pollset_set.cc",
     "src/src/core/lib/iomgr/pollset_set_windows.cc",
     "src/src/core/lib/iomgr/pollset_windows.cc",
@@ -1592,6 +1588,7 @@
     "src/src/core/lib/iomgr/tcp_server_windows.cc",
     "src/src/core/lib/iomgr/tcp_windows.cc",
     "src/src/core/lib/iomgr/time_averaged_stats.cc",
+    "src/src/core/lib/iomgr/timer.cc",
     "src/src/core/lib/iomgr/timer_generic.cc",
     "src/src/core/lib/iomgr/timer_heap.cc",
     "src/src/core/lib/iomgr/timer_manager.cc",
@@ -1611,6 +1608,7 @@
     "src/src/core/lib/profiling/stap_timers.cc",
     "src/src/core/lib/promise/activity.cc",
     "src/src/core/lib/promise/sleep.cc",
+    "src/src/core/lib/resolver/resolver.cc",
     "src/src/core/lib/resolver/resolver_registry.cc",
     "src/src/core/lib/resolver/server_address.cc",
     "src/src/core/lib/resource_quota/api.cc",
@@ -1667,7 +1665,7 @@
     "src/src/core/lib/security/security_connector/fake/fake_security_connector.cc",
     "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.cc",
     "src/src/core/lib/security/security_connector/load_system_roots_fallback.cc",
-    "src/src/core/lib/security/security_connector/load_system_roots_linux.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.cc",
     "src/src/core/lib/security/security_connector/local/local_security_connector.cc",
     "src/src/core/lib/security/security_connector/security_connector.cc",
     "src/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc",
@@ -1787,6 +1785,7 @@
     "src/src/cpp/server/health/health_check_service.cc",
     "src/src/cpp/server/health/health_check_service_server_builder_option.cc",
     "src/src/cpp/server/insecure_server_credentials.cc",
+    "src/src/cpp/server/orca/call_metric_recorder.cc",
     "src/src/cpp/server/secure_server_credentials.cc",
     "src/src/cpp/server/server_builder.cc",
     "src/src/cpp/server/server_callback.cc",
@@ -1862,13 +1861,8 @@
     "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/matcher.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/regex.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/string.upbdefs.c",
+    "src/src/core/lib/event_engine/trace.cc",
     "src/src/core/lib/gprpp/time.cc",
-    "src/src/core/lib/iomgr/event_engine/endpoint.cc",
-    "src/src/core/lib/iomgr/iomgr.cc",
-    "src/src/core/lib/iomgr/pollset.cc",
-    "src/src/core/lib/iomgr/timer.cc",
-    "src/src/core/lib/resolver/resolver.cc",
-    "src/src/core/lib/resource_quota/trace.cc",
 
     # "src/src/core/lib/security/authorization/matchers.cc",
     "src/src/core/lib/security/util/json_util.cc",
@@ -1902,6 +1896,7 @@
   sources = [
     "src/src/core/ext/upb-generated/xds/annotations/v3/status.upb.c",
     "src/src/core/ext/upbdefs-generated/xds/annotations/v3/status.upbdefs.c",
+    "src/src/core/lib/resource_quota/trace.cc",
   ]
 
   deps = [
diff --git a/third_party/grpc/README.chromium b/third_party/grpc/README.chromium
index a0884245..1d45b7a 100644
--- a/third_party/grpc/README.chromium
+++ b/third_party/grpc/README.chromium
@@ -1,8 +1,8 @@
 Name: grpc
 URL: https://github.com/grpc/grpc
 License: Apache 2.0
-Version: v1.47.0-dev
-Revision: d1338d8751231bdc0d87e732d25420e87d24cffd
+Version: v1.48.0-dev
+Revision: 1be6e2c9ebfb4a26bebe6b3f3c45cffc70e71b68
 Security Critical: yes
 
 Please note that that the use of gRPC is not generally allowed within Chromium.
diff --git a/third_party/updater/chromium_mac_amd64/3pp/fetch.py b/third_party/updater/chromium_mac_amd64/3pp/fetch.py
index ad7c4d4..1f3708a3 100755
--- a/third_party/updater/chromium_mac_amd64/3pp/fetch.py
+++ b/third_party/updater/chromium_mac_amd64/3pp/fetch.py
@@ -14,7 +14,7 @@
 # MIN_VERSION is the earliest working version of the updater for self-update
 # testing. If a backwards-incompatible change to the updater is made, it may be
 # necessary to increase the version.
-MIN_VERSION = 1017743
+MIN_VERSION = 1018205
 
 def get_platform():
     return 'Mac'
diff --git a/third_party/updater/chromium_mac_amd64/3pp/install.sh b/third_party/updater/chromium_mac_amd64/3pp/install.sh
index 81cd3dea..d30d200 100755
--- a/third_party/updater/chromium_mac_amd64/3pp/install.sh
+++ b/third_party/updater/chromium_mac_amd64/3pp/install.sh
@@ -11,8 +11,4 @@
 # The commands below should output the built product to this directory.
 PREFIX="$1"
 
-if [ -f "updater/ChromiumUpdater_test.app" ]; then
-  mv updater/ChromiumUpdater_test.app "$PREFIX"
-elif [ -f "updater/ChromiumUpdater.app" ]; then
-  mv updater/ChromiumUpdater.app "$PREFIX"
-fi
+mv updater/ChromiumUpdater_test.app "$PREFIX"
diff --git a/third_party/updater/chromium_mac_arm64/3pp/fetch.py b/third_party/updater/chromium_mac_arm64/3pp/fetch.py
index 894343e..462f695f 100755
--- a/third_party/updater/chromium_mac_arm64/3pp/fetch.py
+++ b/third_party/updater/chromium_mac_arm64/3pp/fetch.py
@@ -14,7 +14,7 @@
 # MIN_VERSION is the earliest working version of the updater for self-update
 # testing. If a backwards-incompatible change to the updater is made, it may be
 # necessary to increase the version.
-MIN_VERSION = 1017727
+MIN_VERSION = 1018198
 
 def get_platform():
     return 'Mac_Arm'
diff --git a/third_party/updater/chromium_mac_arm64/3pp/install.sh b/third_party/updater/chromium_mac_arm64/3pp/install.sh
index 81cd3dea..d30d200 100755
--- a/third_party/updater/chromium_mac_arm64/3pp/install.sh
+++ b/third_party/updater/chromium_mac_arm64/3pp/install.sh
@@ -11,8 +11,4 @@
 # The commands below should output the built product to this directory.
 PREFIX="$1"
 
-if [ -f "updater/ChromiumUpdater_test.app" ]; then
-  mv updater/ChromiumUpdater_test.app "$PREFIX"
-elif [ -f "updater/ChromiumUpdater.app" ]; then
-  mv updater/ChromiumUpdater.app "$PREFIX"
-fi
+mv updater/ChromiumUpdater_test.app "$PREFIX"
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version
index 2433fda..d87047bd 100644
--- a/tools/cast3p/cast_core.version
+++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@
-cast_20220610_0600_RC01
+cast_20220624_0600_RC00
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 620b74b..464fa52 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -369,6 +369,7 @@
       'Win x64 Builder (reclient compare)': 'gpu_tests_release_bot_minimal_symbols_reclient',
       'Win x64 Builder (reclient)': 'gpu_tests_release_bot_minimal_symbols_reclient',
       'Win x64 Builder (reclient)(cross)': 'gpu_tests_release_bot_minimal_symbols_reclient_win_cross',
+      'Win x64 Builder (reclient)(cross)(gvisor)': 'gpu_tests_release_bot_minimal_symbols_reclient_win_cross',
       'android-backuprefptr-arm-fyi-rel': 'release_trybot_backuprefptr_arm_reclient',
       'android-backuprefptr-arm64-fyi-rel': 'release_trybot_backuprefptr_arm64_reclient',
       'android-code-coverage': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_java_coverage_reclient',
@@ -661,6 +662,7 @@
       'Mac Builder reclient test': 'gpu_tests_release_bot_minimal_symbols_reclient',
       'Simple Chrome Builder reclient staging': 'chromeos_amd64-generic-vm_use_fake_dbus_clients_reclient',
       'Simple Chrome Builder reclient test': 'chromeos_amd64-generic-vm_use_fake_dbus_clients_reclient',
+      'Win x64 Builder reclient gVisor cross test': 'gpu_tests_release_bot_minimal_symbols_reclient_win_cross',
       'Win x64 Builder reclient staging': 'gpu_tests_release_bot_minimal_symbols_reclient',
       'Win x64 Builder reclient test': 'gpu_tests_release_bot_minimal_symbols_reclient',
       'ios-simulator reclient staging': 'ios_simulator_debug_static_bot_xctest_reclient',
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 47f1834..ba3cb696 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -537,6 +537,18 @@
       "use_remoteexec": true
     }
   },
+  "Win x64 Builder (reclient)(cross)(gvisor)": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "rbe_cfg_dir": "../../buildtools/reclient_cfgs/win-cross-experiments",
+      "symbol_level": 1,
+      "use_remoteexec": true
+    }
+  },
   "android-backuprefptr-arm-fyi-rel": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/mb/mb_config_expectations/chromium.reclient.fyi.json b/tools/mb/mb_config_expectations/chromium.reclient.fyi.json
index b672947..99f3ca8 100644
--- a/tools/mb/mb_config_expectations/chromium.reclient.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.reclient.fyi.json
@@ -61,6 +61,18 @@
       "use_remoteexec": true
     }
   },
+  "Win x64 Builder reclient gVisor cross test": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "rbe_cfg_dir": "../../buildtools/reclient_cfgs/win-cross-experiments",
+      "symbol_level": 1,
+      "use_remoteexec": true
+    }
+  },
   "Win x64 Builder reclient staging": {
     "gn_args": {
       "dcheck_always_on": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index aa84ab8..5249b4f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10677,13 +10677,15 @@
   <int value="0" label="Battery discharging"/>
   <int value="1" label="Battery plugged-in"/>
   <int value="2" label="Power state changed"/>
-  <int value="3" label="Charge level unavailable"/>
+  <int value="3" label="Error occurred while retrieving battery information"/>
   <int value="4" label="No battery"/>
   <int value="5" label="Battery level increased while disconnected"/>
   <int value="6" label="Invalid interval"/>
   <int value="7"
       label="Discharge rate could not be evaluated because Mac is fully
              charged"/>
+  <int value="8" label="More than 1 battery"/>
+  <int value="9" label="The battery's fully charged capacity is 0."/>
 </enum>
 
 <enum name="BatteryInfoSampleResult">
@@ -56092,6 +56094,7 @@
   <int value="-1454397907" label="OptimizationHints:disabled"/>
   <int value="-1454264660" label="ProminentDarkModeActiveTabTitle:disabled"/>
   <int value="-1453647118" label="PasswordLeakDetection:disabled"/>
+  <int value="-1452786530" label="ChromeOSZramWriteback:enabled"/>
   <int value="-1452010225" label="WindowNaming:disabled"/>
   <int value="-1451644187" label="OverviewSwipeToClose:enabled"/>
   <int value="-1450657485" label="EnableHardwareMirrorMode:enabled"/>
@@ -57149,6 +57152,7 @@
   <int value="-785528415" label="FedCm:disabled"/>
   <int value="-784199026" label="EnableFilesAppCopyImage:enabled"/>
   <int value="-783890018" label="LacrosProfileMigrationForAnyUser:disabled"/>
+  <int value="-783093620" label="WebViewHitTestInBlinkOnTouchStart:enabled"/>
   <int value="-780798969" label="disable-single-click-autofill"/>
   <int value="-778126349" label="DownloadsLocationChange:enabled"/>
   <int value="-778098896" label="EnableAggregatedMlSearchRanking:disabled"/>
@@ -59356,6 +59360,7 @@
   <int value="663294302" label="ForceUseAPDownloadProtection:disabled"/>
   <int value="664363259" label="SiteCharacteristicsDatabase:enabled"/>
   <int value="664591021" label="EnableContinueReading:enabled"/>
+  <int value="665320858" label="WebViewHitTestInBlinkOnTouchStart:disabled"/>
   <int value="665409384"
       label="AutofillToolkitViewsCreditCardDialogsMac:enabled"/>
   <int value="665647051" label="DiscountConsentV2:enabled"/>
@@ -60101,6 +60106,7 @@
   <int value="1152491093" label="MediaSessionService:disabled"/>
   <int value="1153454438"
       label="AutofillCreditCardLastUsedDateDisplay:disabled"/>
+  <int value="1153658720" label="ChromeOSZramWriteback:disabled"/>
   <int value="1154722867" label="TranslateAssistContent:disabled"/>
   <int value="1154790184" label="ScanAppMultiPageScan:enabled"/>
   <int value="1155905651" label="DrawVerticallyEdgeToEdge:disabled"/>
diff --git a/tools/metrics/histograms/metadata/event/histograms.xml b/tools/metrics/histograms/metadata/event/histograms.xml
index d9133da..3a87832 100644
--- a/tools/metrics/histograms/metadata/event/histograms.xml
+++ b/tools/metrics/histograms/metadata/event/histograms.xml
@@ -1725,7 +1725,7 @@
 </histogram>
 
 <histogram name="Event.UserActivation.TriggerFor{ActivationGatedApiType}"
-    enum="UserActivationTriggerEnum" expires_after="2022-06-30">
+    enum="UserActivationTriggerEnum" expires_after="2022-12-31">
   <owner>mustaq@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index 2e21078..908e32a2d 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="LanguageDetection.TFLiteModel.LanguageDetectionModelState"
-    enum="LanguageDetectionModelState" expires_after="2022-06-30">
+    enum="LanguageDetectionModelState" expires_after="2023-06-30">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -33,7 +33,7 @@
 </histogram>
 
 <histogram name="LanguageDetection.TFLiteModel.WasModelAvailableForDetection"
-    enum="BooleanAvailable" expires_after="2022-11-13">
+    enum="BooleanAvailable" expires_after="2023-06-30">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -44,7 +44,7 @@
 </histogram>
 
 <histogram name="LanguageDetection.TFLiteModel.WasModelRequestDeferred"
-    enum="BooleanDeferred" expires_after="2022-11-13">
+    enum="BooleanDeferred" expires_after="2023-06-30">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -56,7 +56,7 @@
 
 <histogram
     name="LanguageDetection.TFLiteModel.WasModelUnavailableDueToDeferredLoad"
-    enum="Boolean" expires_after="2022-06-30">
+    enum="Boolean" expires_after="2023-06-30">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index f7729e4..47c212c 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -801,6 +801,50 @@
 </histogram>
 
 <histogram
+    name="PageLoad.Clients.GoogleSearch.PaintTiming.NavigationToFirstContentfulPaint"
+    units="ms" expires_after="2022-12-23">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <summary>
+    Measures the time in milliseconds from navigation timing's navigation start
+    to the time when the page first paints content, for Google Search page
+    loads. A contentful paint includes a paint of text, image, SVG, or canvas.
+    Only recorded for Search navigations that start in the foreground and stay
+    in the foreground until the first contentful paint.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.GoogleSearch.PaintTiming.NavigationToLargestContentfulPaint"
+    units="ms" expires_after="2022-12-23">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <summary>
+    Measures the time in milliseconds from navigation timing's navigation start
+    to the time when the page first paints the experimental largest content
+    (text or image) within viewport, for Google Search page loads. See
+    http://bit.ly/largest_contentful_paint_explainer for more details. Differs
+    from SubFrame.PaintTiming.NavigationToLargestContentfulPaint in that removed
+    content is still considered a valid candidate. Only recorded for Search
+    navigations that happen entirely in the foreground. The metric is emitted
+    when the navigation is completed or the app is backgrounded on Android.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.GoogleSearch.ParseTiming.NavigationToParseStart"
+    units="ms" expires_after="2022-12-23">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <summary>
+    Measures the time from navigation timing's navigation start to the time the
+    parser started, for Google Search page loads. Only recorded for Search
+    navigations that start in the foreground and stay in the foreground until
+    parse start.
+  </summary>
+</histogram>
+
+<histogram
     name="PageLoad.Clients.Prerender.InteractiveTiming.FirstInputDelay4{PrerenderTriggerType}"
     units="ms" expires_after="2022-11-27">
   <owner>ksakamoto@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index a43c3d36..c153538 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -654,7 +654,7 @@
   </summary>
 </histogram>
 
-<histogram name="Power.BatteryDischargeMode{UsageScenario}"
+<histogram name="Power.BatteryDischargeMode2{UsageScenario}"
     enum="BatteryDischargeMode" expires_after="2022-11-30">
   <owner>etiennep@chromium.org</owner>
   <owner>olivierli@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index a5f1715..fe7decc 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "15bc5ac9a2f32134b698d0cd242d00715cb7c16c",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/588d85dc56ee732822f5ba6c09ca0bf43a7a7c24/trace_processor_shell.exe"
+            "hash": "adcf9f60e35908138ad440207ded004627ccf43a",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/010e6c1867a689266316ad607725cfd8ed11d3f9/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "e4b4daabc84baf394356d8106740708170ee514f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/588d85dc56ee732822f5ba6c09ca0bf43a7a7c24/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/010e6c1867a689266316ad607725cfd8ed11d3f9/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "cd36bf5e5151252ac1b88bf8fc029c06ef2e8570",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/588d85dc56ee732822f5ba6c09ca0bf43a7a7c24/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/010e6c1867a689266316ad607725cfd8ed11d3f9/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/clipboard/custom_data_helper.cc b/ui/base/clipboard/custom_data_helper.cc
index def5a11..443996b4 100644
--- a/ui/base/clipboard/custom_data_helper.cc
+++ b/ui/base/clipboard/custom_data_helper.cc
@@ -20,7 +20,7 @@
 bool SkipString16(base::PickleIterator* iter) {
   DCHECK(iter);
 
-  size_t len;
+  int len;
   if (!iter->ReadLength(&len))
     return false;
   return iter->SkipBytes(len * sizeof(char16_t));
diff --git a/ui/chromeos/translations/ui_chromeos_strings_da.xtb b/ui/chromeos/translations/ui_chromeos_strings_da.xtb
index 663ce295..3a8d70a5 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_da.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_da.xtb
@@ -340,6 +340,7 @@
 <translation id="3971140002794351170">Download mobilprofil. Netværk <ph name="NETWORK_INDEX" /> af <ph name="NETWORK_COUNT" />, <ph name="NETWORK_NAME" />, <ph name="NETWORK_PROVIDER_NAME" /></translation>
 <translation id="3973925058222872294">Engelsk (Storbritannien)</translation>
 <translation id="3975895378829046965">Bangla fonetisk</translation>
+<translation id="3991446849494482687">Denne handling deaktiverer også indstillingen for SIM-lås på enheden.</translation>
 <translation id="4002066346123236978">Titel</translation>
 <translation id="4017788180641807848">Engelsk (USA) med Workman-tastatur</translation>
 <translation id="4040753847560036377">PUK-koden er forkert</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_mn.xtb b/ui/chromeos/translations/ui_chromeos_strings_mn.xtb
index a3a56eb3..7c231f6 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_mn.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_mn.xtb
@@ -340,6 +340,7 @@
 <translation id="3971140002794351170">Мобайл профайл татах, <ph name="NETWORK_COUNT" />-н <ph name="NETWORK_INDEX" /> сүлжээ, <ph name="NETWORK_NAME" />, <ph name="NETWORK_PROVIDER_NAME" /></translation>
 <translation id="3973925058222872294">Англи (UK)</translation>
 <translation id="3975895378829046965">Бенгал авиа зүй</translation>
+<translation id="3991446849494482687">Энэ нь мөн төхөөрөмж дээрх SIM түгжээний тохиргоог идэвхгүй болгоно.</translation>
 <translation id="4002066346123236978">Гарчиг</translation>
 <translation id="4017788180641807848">Workman гартай англи (АНУ)</translation>
 <translation id="4040753847560036377">PUK буруу байна</translation>
diff --git a/ui/chromeos/translations/ui_chromeos_strings_uz.xtb b/ui/chromeos/translations/ui_chromeos_strings_uz.xtb
index 8a9a12f..ef8bca2 100644
--- a/ui/chromeos/translations/ui_chromeos_strings_uz.xtb
+++ b/ui/chromeos/translations/ui_chromeos_strings_uz.xtb
@@ -340,6 +340,7 @@
 <translation id="3971140002794351170">Mobil profil, tarmoq <ph name="NETWORK_INDEX" /><ph name="NETWORK_COUNT" />, <ph name="NETWORK_NAME" />, <ph name="NETWORK_PROVIDER_NAME" /> kabilarni yuklab olish</translation>
 <translation id="3973925058222872294">Ingliz (Buyuk Britaniya)</translation>
 <translation id="3975895378829046965">Bangla (Fonetik)</translation>
+<translation id="3991446849494482687">Bunda qurilmadagi SIM kartani qulflash sozlamasi ham faolsizlantiriladi.</translation>
 <translation id="4002066346123236978">Nomi</translation>
 <translation id="4017788180641807848">Ingliz (AQSH) va Workman klaviaturasi</translation>
 <translation id="4040753847560036377">PUK kod xato</translation>
diff --git a/ui/display/win/screen_win.cc b/ui/display/win/screen_win.cc
index 65deacd6..f6184b0 100644
--- a/ui/display/win/screen_win.cc
+++ b/ui/display/win/screen_win.cc
@@ -318,14 +318,14 @@
   } else if (hdr_enabled_on_any_display) {
     float sdr_white_level = display_info.sdr_white_level();
     float hdr_max_luminance_relative = 0.f;
-    if (dxgi_output_desc) {
+    if (dxgi_output_desc && dxgi_output_desc->hdr_enabled) {
       hdr_max_luminance_relative =
           dxgi_output_desc->max_luminance / sdr_white_level;
-      if (!dxgi_output_desc->hdr_enabled)
-        sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel;
     }
     hdr_max_luminance_relative = std::max(hdr_max_luminance_relative,
                                           kMinHDRCapableMaxLuminanceRelative);
+    // TODO(https://crbug.com/1339352): Do not allow non-HDR-enabled displays
+    // to use HDR color spaces.
     color_spaces = GetDisplayColorSpacesForHdr(sdr_white_level,
                                                hdr_max_luminance_relative);
   } else {
diff --git a/ui/gfx/geometry/mask_filter_info.cc b/ui/gfx/geometry/mask_filter_info.cc
index 77621089..dd56b66 100644
--- a/ui/gfx/geometry/mask_filter_info.cc
+++ b/ui/gfx/geometry/mask_filter_info.cc
@@ -17,15 +17,19 @@
   if (!transform.TransformRRectF(&rounded_corner_bounds_))
     return false;
 
-  if (!gradient_mask_.IsEmpty())
-    gradient_mask_.Transform(transform);
+  if (gradient_mask_ && !gradient_mask_->IsEmpty())
+    gradient_mask_->Transform(transform);
 
   return true;
 }
 
 std::string MaskFilterInfo::ToString() const {
-  return "MaskFilterInfo{" + rounded_corner_bounds_.ToString() +
-         ", gradient_mask=" + gradient_mask_.ToString() + "}";
+  std::string result = "MaskFilterInfo{" + rounded_corner_bounds_.ToString();
+
+  if (gradient_mask_)
+    result += ", gradient_mask=" + gradient_mask_->ToString() + "}";
+
+  return result;
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/mask_filter_info.h b/ui/gfx/geometry/mask_filter_info.h
index 6c33e8c..37da7d7 100644
--- a/ui/gfx/geometry/mask_filter_info.h
+++ b/ui/gfx/geometry/mask_filter_info.h
@@ -5,6 +5,7 @@
 #ifndef UI_GFX_GEOMETRY_MASK_FILTER_INFO_H_
 #define UI_GFX_GEOMETRY_MASK_FILTER_INFO_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
 #include "ui/gfx/geometry/linear_gradient.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -41,11 +42,16 @@
            rounded_corner_bounds_.GetType() != RRectF::Type::kRect;
   }
 
-  const gfx::LinearGradient& gradient_mask() const { return gradient_mask_; }
+  const absl::optional<gfx::LinearGradient>& gradient_mask() const {
+    return gradient_mask_;
+  }
 
   // True if this contains an effective gradient mask (requires filter bounds).
   bool HasGradientMask() const {
-    return !rounded_corner_bounds_.IsEmpty() && !gradient_mask_.IsEmpty();
+    if (rounded_corner_bounds_.IsEmpty())
+      return false;
+
+    return gradient_mask_ && !gradient_mask_->IsEmpty();
   }
 
   // True if this contains no effective mask information.
@@ -63,7 +69,7 @@
   RRectF rounded_corner_bounds_;
 
   // Shader based linear gradient mask to be applied to a layer.
-  gfx::LinearGradient gradient_mask_ = gfx::LinearGradient::GetEmpty();
+  absl::optional<gfx::LinearGradient> gradient_mask_;
 };
 
 inline bool operator==(const MaskFilterInfo& lhs, const MaskFilterInfo& rhs) {
diff --git a/ui/gfx/ipc/skia/gfx_skia_param_traits.cc b/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
index 844e357..21a3b7f8 100644
--- a/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
+++ b/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
@@ -65,7 +65,8 @@
 void ParamTraits<SkBitmap>::Write(base::Pickle* m, const SkBitmap& p) {
   WriteParam(m, p.info());
   size_t pixel_size = p.computeByteSize();
-  m->WriteData(reinterpret_cast<const char*>(p.getPixels()), pixel_size);
+  m->WriteData(reinterpret_cast<const char*>(p.getPixels()),
+               static_cast<int>(pixel_size));
 }
 
 bool ParamTraits<SkBitmap>::Read(const base::Pickle* m,
@@ -76,14 +77,15 @@
     return false;
 
   const char* bitmap_data;
-  size_t bitmap_data_size = 0;
+  int bitmap_data_size = 0;
   if (!iter->ReadData(&bitmap_data, &bitmap_data_size))
     return false;
+  // ReadData() only returns true if bitmap_data_size >= 0.
 
   if (!r->tryAllocPixels(image_info))
     return false;
 
-  if (bitmap_data_size != r->computeByteSize())
+  if (static_cast<size_t>(bitmap_data_size) != r->computeByteSize())
     return false;
   memcpy(r->getPixels(), bitmap_data, bitmap_data_size);
   return true;
diff --git a/ui/gfx/mojom/mask_filter_info.mojom b/ui/gfx/mojom/mask_filter_info.mojom
index 55bd480..9d42d6e0 100644
--- a/ui/gfx/mojom/mask_filter_info.mojom
+++ b/ui/gfx/mojom/mask_filter_info.mojom
@@ -5,9 +5,10 @@
 module gfx.mojom;
 
 import "ui/gfx/mojom/rrect_f.mojom";
+import "ui/gfx/mojom/linear_gradient.mojom";
 
-// See ui/gfx/mask_filter_info.h.
+// See ui/gfx/geometry/mask_filter_info.h.
 struct MaskFilterInfo {
   gfx.mojom.RRectF rounded_corner_bounds;
-  // TODO(crbug.com/1039003): add gradient mask field
+  gfx.mojom.LinearGradient? gradient_mask;
 };
diff --git a/ui/gfx/mojom/mask_filter_info_mojom_traits.cc b/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
index 683762a..a7d4a7af 100644
--- a/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
+++ b/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
@@ -12,7 +12,15 @@
   gfx::RRectF bounds;
   if (!data.ReadRoundedCornerBounds(&bounds))
     return false;
-  *out = gfx::MaskFilterInfo(bounds);
+
+  absl::optional<gfx::LinearGradient> gradient_mask;
+  if (!data.ReadGradientMask(&gradient_mask))
+    return false;
+
+  if (gradient_mask && !gradient_mask->IsEmpty())
+    *out = gfx::MaskFilterInfo(bounds, gradient_mask.value());
+  else
+    *out = gfx::MaskFilterInfo(bounds);
   return true;
 }
 
diff --git a/ui/gfx/mojom/mask_filter_info_mojom_traits.h b/ui/gfx/mojom/mask_filter_info_mojom_traits.h
index 849742a..0bb5300 100644
--- a/ui/gfx/mojom/mask_filter_info_mojom_traits.h
+++ b/ui/gfx/mojom/mask_filter_info_mojom_traits.h
@@ -5,7 +5,9 @@
 #ifndef UI_GFX_MOJOM_MASK_FILTER_INFO_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_MASK_FILTER_INFO_MOJOM_TRAITS_H_
 
+#include "ui/gfx/geometry/linear_gradient.h"
 #include "ui/gfx/geometry/mask_filter_info.h"
+#include "ui/gfx/mojom/linear_gradient_mojom_traits.h"
 #include "ui/gfx/mojom/mask_filter_info.mojom-shared.h"
 #include "ui/gfx/mojom/rrect_f_mojom_traits.h"
 
@@ -17,6 +19,11 @@
     return info.rounded_corner_bounds();
   }
 
+  static const absl::optional<gfx::LinearGradient>& gradient_mask(
+      const gfx::MaskFilterInfo& info) {
+    return info.gradient_mask();
+  }
+
   static bool Read(gfx::mojom::MaskFilterInfoDataView data,
                    gfx::MaskFilterInfo* out);
 };
diff --git a/ui/gl/gl_bindings.h b/ui/gl/gl_bindings.h
index 9ef2e33..65ef00ae 100644
--- a/ui/gl/gl_bindings.h
+++ b/ui/gl/gl_bindings.h
@@ -193,9 +193,6 @@
 // GL_CHROMIUM_texture_filtering_hint
 #define GL_TEXTURE_FILTERING_HINT_CHROMIUM               0x8AF0
 
-// GL_CHROMIUM_texture_storage_image
-#define GL_SCANOUT_CHROMIUM 0x6000
-
 // GL_OES_texure_3D
 #define GL_SAMPLER_3D_OES                                0x8B5F
 
diff --git a/ui/gl/gl_enums_implementation_autogen.h b/ui/gl/gl_enums_implementation_autogen.h
index 02e8d9a..eff22cc6 100644
--- a/ui/gl/gl_enums_implementation_autogen.h
+++ b/ui/gl/gl_enums_implementation_autogen.h
@@ -965,10 +965,6 @@
         "GL_MULTISAMPLE_BUFFER_BIT6_QCOM",
     },
     {
-        0x6000,
-        "GL_SCANOUT_CHROMIUM",
-    },
-    {
         0x6003,
         "GL_GET_ERROR_QUERY_CHROMIUM",
     },
diff --git a/ui/ozone/platform/wayland/host/wayland_event_source.cc b/ui/ozone/platform/wayland/host/wayland_event_source.cc
index 4a7f275..aece718 100644
--- a/ui/ozone/platform/wayland/host/wayland_event_source.cc
+++ b/ui/ozone/platform/wayland/host/wayland_event_source.cc
@@ -29,6 +29,7 @@
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
 #include "ui/ozone/platform/wayland/host/wayland_event_watcher.h"
 #include "ui/ozone/platform/wayland/host/wayland_keyboard.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -49,6 +50,40 @@
           static_cast<uint8_t>(value >> 16), static_cast<uint8_t>(value >> 24)};
 }
 
+EventTarget* GetRootTarget(EventTarget* target) {
+  EventTarget* parent = target->GetParentTarget();
+  return parent ? GetRootTarget(parent) : target;
+}
+
+gfx::Point GetOriginInScreen(WaylandWindow* target) {
+  gfx::Point origin = target->GetBoundsInDIP().origin();
+  WaylandWindow* parent =
+      static_cast<WaylandWindow*>(target->GetParentTarget());
+  while (parent) {
+    origin += parent->GetBoundsInDIP().origin().OffsetFromOrigin();
+    parent = static_cast<WaylandWindow*>(parent->GetParentTarget());
+  }
+  return origin;
+}
+
+gfx::Point GetLocationInScreen(LocatedEvent* event) {
+  WaylandWindow* root_window =
+      static_cast<WaylandWindow*>(GetRootTarget(event->target()));
+  return event->root_location() +
+         root_window->GetBoundsInDIP().origin().OffsetFromOrigin();
+}
+
+void SetRootLocation(LocatedEvent* event) {
+  gfx::PointF location = event->location_f();
+  WaylandWindow* target = static_cast<WaylandWindow*>(event->target());
+
+  while (target->GetParentTarget()) {
+    location += target->GetBoundsInDIP().origin().OffsetFromOrigin();
+    target = static_cast<WaylandWindow*>(target->GetParentTarget());
+  }
+  event->set_root_location_f(location);
+}
+
 // Number of fingers for scroll gestures.
 constexpr int kGestureScrollFingerCount = 2;
 
@@ -89,6 +124,16 @@
 
 // WaylandEventSource implementation
 
+// static
+void WaylandEventSource::ConvertEventToTarget(const EventTarget* new_target,
+                                              LocatedEvent* event) {
+  WaylandWindow* current_target = static_cast<WaylandWindow*>(event->target());
+  gfx::Vector2d diff = GetOriginInScreen(current_target) -
+                       GetOriginInScreen(static_cast<WaylandWindow*>(
+                           const_cast<EventTarget*>(new_target)));
+  event->set_location_f(event->location_f() + diff);
+}
+
 WaylandEventSource::WaylandEventSource(wl_display* display,
                                        wl_event_queue* event_queue,
                                        WaylandWindowManager* window_manager,
@@ -200,15 +245,21 @@
     // Save new pointer location.
     pointer_location_ = location;
     window_manager_->SetPointerFocusedWindow(window);
+    current_pointer_frame_.target = window;
   }
 
-  EventType type = focused ? ET_MOUSE_ENTERED : ET_MOUSE_EXITED;
-  MouseEvent event(type, pointer_location_, pointer_location_,
-                   EventTimeForNow(), pointer_flags_, 0);
-  DispatchEvent(&event);
+  auto* target = window_manager_->GetCurrentPointerFocusedWindow();
+  if (target) {
+    EventType type = focused ? ET_MOUSE_ENTERED : ET_MOUSE_EXITED;
+    MouseEvent event(type, pointer_location_, pointer_location_,
+                     EventTimeForNow(), pointer_flags_, 0);
+    SetTargetAndDispatchEvent(&event, target);
+  }
 
-  if (!focused)
+  if (!focused) {
     window_manager_->SetPointerFocusedWindow(nullptr);
+    current_pointer_frame_.target = nullptr;
+  }
 }
 
 void WaylandEventSource::OnPointerButtonEvent(EventType type,
@@ -231,7 +282,11 @@
   MouseEvent event(type, pointer_location_, pointer_location_,
                    EventTimeForNow(), flags, changed_button,
                    PointerDetailsForDispatching());
-  DispatchEvent(&event);
+
+  auto* target = window_manager_->GetCurrentPointerFocusedWindow();
+  // A window may be deleted when the event arrived from the server.
+  if (target)
+    SetTargetAndDispatchEvent(&event, target);
 
   if (window)
     window_manager_->SetPointerFocusedWindow(prev_focused_window);
@@ -243,7 +298,12 @@
   int flags = pointer_flags_ | keyboard_modifiers_;
   MouseEvent event(ET_MOUSE_MOVED, pointer_location_, pointer_location_,
                    EventTimeForNow(), flags, 0, PointerDetailsForDispatching());
-  DispatchEvent(&event);
+  auto* target = window_manager_->GetCurrentPointerFocusedWindow();
+
+  // A window may be deleted when the event arrived from the server.
+  if (!target)
+    return;
+  SetTargetAndDispatchEvent(&event, target);
 }
 
 void WaylandEventSource::OnPointerAxisEvent(const gfx::Vector2dF& offset) {
@@ -276,6 +336,9 @@
 #else
       false;
 #endif
+  auto* target = current_pointer_frame_.target;
+  if (!window_manager_->IsWindowValid(target))
+    return;
 
   // Dispatch Fling event if pointer.axis_stop is notified and the recent
   // pointer.axis events meets the criteria to start fling scroll.
@@ -289,7 +352,7 @@
         vx == 0 && vy == 0 ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START,
         pointer_location_, pointer_location_, now, flags, vx, vy, vx, vy,
         kGestureScrollFingerCount);
-    DispatchEvent(&event);
+    SetTargetAndDispatchEvent(&event, target);
     recent_pointer_frames_.clear();
   } else if (current_pointer_frame_.axis_source) {
     if (*current_pointer_frame_.axis_source == WL_POINTER_AXIS_SOURCE_WHEEL ||
@@ -298,7 +361,7 @@
       MouseWheelEvent event(
           gfx::Vector2d(current_pointer_frame_.dx, current_pointer_frame_.dy),
           pointer_location_, pointer_location_, EventTimeForNow(), flags, 0);
-      DispatchEvent(&event);
+      SetTargetAndDispatchEvent(&event, target);
     } else if (*current_pointer_frame_.axis_source ==
                    WL_POINTER_AXIS_SOURCE_FINGER ||
                *current_pointer_frame_.axis_source ==
@@ -307,7 +370,7 @@
                         EventTimeForNow(), flags, current_pointer_frame_.dx,
                         current_pointer_frame_.dy, current_pointer_frame_.dx,
                         current_pointer_frame_.dy, kGestureScrollFingerCount);
-      DispatchEvent(&event);
+      SetTargetAndDispatchEvent(&event, target);
     }
 
     if (recent_pointer_frames_.size() + 1 > kRecentPointerFrameMaxSize)
@@ -378,7 +441,7 @@
   TouchEvent event(ET_TOUCH_RELEASED, location, location, timestamp, details,
                    keyboard_modifiers_);
   if (dispatch_policy == EventDispatchPolicy::kImmediate) {
-    DispatchEvent(&event);
+    SetTouchTargetAndDispatchTouchEvent(&event);
     OnTouchReleaseInternal(id);
   } else {
     touch_frames_.push_front(std::make_unique<TouchFrame>(
@@ -412,6 +475,28 @@
     last_touch_stylus_data_.erase(stylus_data_it);
 }
 
+void WaylandEventSource::SetTargetAndDispatchEvent(Event* event,
+                                                   EventTarget* target) {
+  Event::DispatcherApi(event).set_target(target);
+  if (event->IsLocatedEvent()) {
+    SetRootLocation(event->AsLocatedEvent());
+    auto* cursor_position = connection_->wayland_cursor_position();
+    if (cursor_position) {
+      cursor_position->OnCursorPositionChanged(
+          GetLocationInScreen(event->AsLocatedEvent()));
+    }
+  }
+  DispatchEvent(event);
+}
+
+void WaylandEventSource::SetTouchTargetAndDispatchTouchEvent(
+    TouchEvent* event) {
+  auto iter = touch_points_.find(event->pointer_details().id);
+  auto target = iter != touch_points_.end() ? iter->second->window : nullptr;
+
+  SetTargetAndDispatchEvent(event, target.get());
+}
+
 void WaylandEventSource::OnTouchMotionEvent(
     const gfx::PointF& location,
     base::TimeTicks timestamp,
@@ -428,7 +513,7 @@
   TouchEvent event(ET_TOUCH_MOVED, location, location, timestamp, details,
                    keyboard_modifiers_);
   if (dispatch_policy == DispatchPolicy::kImmediate) {
-    DispatchEvent(&event);
+    SetTouchTargetAndDispatchTouchEvent(&event);
   } else {
     touch_frames_.push_front(
         std::make_unique<TouchFrame>(event, base::NullCallback()));
@@ -449,7 +534,7 @@
     PointerId id = touch_point.first;
     TouchEvent event(ET_TOUCH_CANCELLED, location, location, timestamp,
                      PointerDetails(EventPointerType::kTouch, id));
-    DispatchEvent(&event);
+    SetTouchTargetAndDispatchTouchEvent(&event);
     HandleTouchFocusChange(touch_point.second->window, false);
   }
   touch_points_.clear();
@@ -473,7 +558,7 @@
           old_event.time_stamp(), pointer_details_with_stylus_data.value(),
           old_event.flags());
     }
-    DispatchEvent(&(touch_frame->event));
+    SetTouchTargetAndDispatchTouchEvent(&(touch_frame->event));
     if (!touch_frame->completion_cb.is_null())
       std::move(touch_frame->completion_cb).Run();
   }
diff --git a/ui/ozone/platform/wayland/host/wayland_event_source.h b/ui/ozone/platform/wayland/host/wayland_event_source.h
index f15b63c..0bd3141e 100644
--- a/ui/ozone/platform/wayland/host/wayland_event_source.h
+++ b/ui/ozone/platform/wayland/host/wayland_event_source.h
@@ -55,6 +55,9 @@
                            public WaylandZwpPointerGestures::Delegate,
                            public WaylandZwpRelativePointerManager::Delegate {
  public:
+  static void ConvertEventToTarget(const EventTarget* new_target,
+                                   LocatedEvent* event);
+
   WaylandEventSource(wl_display* display,
                      wl_event_queue* event_queue,
                      WaylandWindowManager* window_manager,
@@ -162,6 +165,7 @@
     PointerFrame& operator=(const PointerFrame&);
     PointerFrame& operator=(PointerFrame&&);
 
+    WaylandWindow* target = nullptr;
     absl::optional<uint32_t> axis_source;
     float dx = 0.0f;
     float dy = 0.0f;
@@ -206,6 +210,12 @@
   // Wrap up method to support async touch release processing.
   void OnTouchReleaseInternal(PointerId id);
 
+  // Set the target to the event, then dispatch the event.
+  void SetTargetAndDispatchEvent(Event* event, EventTarget* target);
+
+  // Find and set the target for the touch event, then dispatch the event.
+  void SetTouchTargetAndDispatchTouchEvent(TouchEvent* event);
+
   const raw_ptr<WaylandWindowManager> window_manager_;
 
   const raw_ptr<WaylandConnection> connection_;
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index 923dad6..9c16d03 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -32,7 +32,6 @@
 #include "ui/ozone/common/features.h"
 #include "ui/ozone/platform/wayland/common/wayland_overlay_config.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
-#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
 #include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
 #include "ui/ozone/platform/wayland/host/wayland_event_source.h"
 #include "ui/ozone/platform/wayland/host/wayland_frame_manager.h"
@@ -457,8 +456,6 @@
         connection_->wayland_window_manager()->located_events_grabber();
     auto* root_parent_window = GetRootParentWindow();
 
-    UpdateCursorPositionFromEvent(event);
-
     // We must reroute the events to the event grabber iff these windows belong
     // to the same root parent window. For example, there are 2 top level
     // Wayland windows. One of them (window_1) has a child menu window that is
@@ -473,25 +470,48 @@
         event_grabber &&
         root_parent_window == event_grabber->GetRootParentWindow();
     if (send_to_grabber) {
-      ConvertEventLocationToTargetWindowLocation(
-          event_grabber->GetBoundsInDIP().origin(), GetBoundsInDIP().origin(),
-          event->AsLocatedEvent());
+      ConvertEventToTarget(event_grabber, event->AsLocatedEvent());
+      Event::DispatcherApi(event).set_target(event_grabber);
     }
 
-    // Wayland sends locations in DIP so they need to be translated to
-    // physical pixels.
+    // Wayland sends locations in DIP but dispatch code expects pixels, so they
+    // need to be translated to physical pixels.
     event->AsLocatedEvent()->set_location_f(gfx::ScalePoint(
         event->AsLocatedEvent()->location_f(), window_scale(), window_scale()));
 
     if (send_to_grabber)
-      return event_grabber->DispatchEventToDelegate(native_event);
+      return event_grabber->DispatchEventToDelegate(event);
   }
 
   // Dispatch all keyboard events to the root window.
   if (event->IsKeyEvent())
     return GetRootParentWindow()->DispatchEventToDelegate(event);
 
-  return DispatchEventToDelegate(native_event);
+  return DispatchEventToDelegate(event);
+}
+
+// EventTarget:
+bool WaylandWindow::CanAcceptEvent(const Event& event) {
+  return true;
+}
+
+EventTarget* WaylandWindow::GetParentTarget() {
+  return nullptr;
+}
+
+std::unique_ptr<EventTargetIterator> WaylandWindow::GetChildIterator() const {
+  NOTREACHED();
+  return nullptr;
+}
+
+EventTargeter* WaylandWindow::GetEventTargeter() {
+  return nullptr;
+}
+
+void WaylandWindow::ConvertEventToTarget(const EventTarget* new_target,
+                                         LocatedEvent* event) const {
+  // Move this to wayland event soruce?
+  WaylandEventSource::ConvertEventToTarget(new_target, event);
 }
 
 void WaylandWindow::HandleSurfaceConfigure(uint32_t serial) {
@@ -672,43 +692,6 @@
   // output.
 }
 
-void WaylandWindow::UpdateCursorPositionFromEvent(const Event* orig_event) {
-  DCHECK(orig_event->IsLocatedEvent());
-
-  // This is a tricky part. Initially, Wayland sends events to surfaces the
-  // events are targeted for. But, in order to fulfill Chromium's assumptions
-  // about event targets, some of the events are rerouted and their locations
-  // are converted. The event we got here is rerouted and it has had its
-  // location fixed.
-  //
-  // Basically, this method must translate coordinates of all events
-  // in regards to top-level windows' coordinates as it's always located at
-  // origin (0,0) from Chromium point of view (remember that wl_shell/xdg_shell
-  // doesn't provide global coordinates to its clients). And it's totally fine
-  // to use it as the target. Thus, the location of the |event| is always
-  // converted using the top-level window's bounds as the target excluding
-  // cases, when the mouse/touch is over a top-level window.
-  auto* cursor_position = connection_->wayland_cursor_position();
-  if (!cursor_position)
-    return;
-  const LocatedEvent* located_event = orig_event->AsLocatedEvent();
-  std::unique_ptr<Event> event;
-
-  auto* toplevel_window = GetRootParentWindow();
-  if (toplevel_window != this) {
-    event = Event::Clone(*orig_event);
-    ConvertEventLocationToTargetWindowLocation(
-        toplevel_window->GetBoundsInDIP().origin(), GetBoundsInDIP().origin(),
-        event->AsLocatedEvent());
-
-    located_event = event->AsLocatedEvent();
-  }
-
-  cursor_position->OnCursorPositionChanged(
-      located_event->location() +
-      toplevel_window->GetBoundsInDIP().origin().OffsetFromOrigin());
-}
-
 WaylandWindow* WaylandWindow::GetTopMostChildWindow() {
   return child_window_ ? child_window_->GetTopMostChildWindow() : this;
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_window.h b/ui/ozone/platform/wayland/host/wayland_window.h
index 9ca7d16..e189945 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_window.h
@@ -20,6 +20,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/task/single_thread_task_runner.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
+#include "ui/events/event_target.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/point_f.h"
@@ -52,7 +53,8 @@
 
 class WaylandWindow : public PlatformWindow,
                       public PlatformEventDispatcher,
-                      public WmDragHandler {
+                      public WmDragHandler,
+                      public EventTarget {
  public:
   WaylandWindow(const WaylandWindow&) = delete;
   WaylandWindow& operator=(const WaylandWindow&) = delete;
@@ -204,6 +206,14 @@
   bool CanDispatchEvent(const PlatformEvent& event) override;
   uint32_t DispatchEvent(const PlatformEvent& event) override;
 
+  // EventTarget:
+  bool CanAcceptEvent(const Event& event) override;
+  EventTarget* GetParentTarget() override;
+  std::unique_ptr<EventTargetIterator> GetChildIterator() const override;
+  EventTargeter* GetEventTargeter() override;
+  void ConvertEventToTarget(const EventTarget* target,
+                            LocatedEvent* event) const override;
+
   // Handles the configuration events coming from the shell objects.
   // The width and height come in DIP of the output that the surface is
   // currently bound to.
@@ -375,8 +385,6 @@
   // Initializes the WaylandWindow with supplied properties.
   bool Initialize(PlatformWindowInitProperties properties);
 
-  void UpdateCursorPositionFromEvent(const Event* event);
-
   uint32_t DispatchEventToDelegate(const PlatformEvent& native_event);
 
   // Additional initialization of derived classes.
diff --git a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
index 06b5bfa..161cba2 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
@@ -228,6 +228,7 @@
   pointer_location_ = location;
 
   DCHECK(drag_source_.has_value());
+  // Check if this is necessary.
   if (*drag_source_ == DragSource::kMouse)
     pointer_delegate_->OnPointerFocusChanged(window, location);
   else
@@ -299,15 +300,15 @@
   DVLOG(1) << "OnLeave";
   data_offer_.reset();
 
-  // As Wayland clients are only aware of surface-local coordinates and there is
-  // no implicit grab during DND sessions, a fake motion event with negative
-  // y coordinate is used here to allow higher level UI components to detect
-  // when a window should be detached. E.g: On Chrome, dragging a tab all the
-  // way up to the top edge of the window won't work without this fake motion
-  // event upon wl_data_device::leave events. This is a workaround and should
-  // ideally be reworked in the future, at higher level layers such that they
-  // properly handle platforms that do not support global screen coordinates,
-  // like Wayland.
+  // As wl-shell/xdg-shell clients are only aware of surface-local coordinates
+  // and there is no implicit grab during DND sessions, a fake motion event with
+  // negative y coordinate is used here to allow higher level UI components to
+  // detect when a window should be detached. E.g: On Chrome, dragging a tab all
+  // the way up to the top edge of the window won't work without this fake
+  // motion event upon wl_data_device::leave events. This is a workaround and
+  // should ideally be reworked in the future, at higher level layers such that
+  // they properly handle platforms that do not support global screen
+  // coordinates, like Wayland.
   //
   // TODO(https://crbug.com/1282186): Find a better solution for upwards tab
   // detaching.
@@ -373,6 +374,8 @@
   // In case of touch, though, we simply reset the focus altogether.
   if (IsExtendedDragAvailableInternal() && dragged_window_) {
     if (*drag_source_ == DragSource::kMouse) {
+      // TODO: check if this usage is correct.
+
       pointer_delegate_->OnPointerFocusChanged(dragged_window_,
                                                pointer_location_);
     } else {
@@ -456,15 +459,6 @@
   if (!should_process_drag_event_)
     return;
 
-  // Update current cursor position relative to the event source
-  // (focused window) so it can be retrieved later on through
-  // |Screen::GetCursorScreenPoint| API.
-  auto* pointer_focused_window = connection_->wayland_window_manager()
-                                     ->GetCurrentPointerOrTouchFocusedWindow();
-
-  if (pointer_focused_window)
-    pointer_focused_window->UpdateCursorPositionFromEvent(event);
-
   // Notify listeners about window bounds change (i.e: re-positioning) event.
   // To do so, set the new bounds as per the motion event location and the drag
   // offset. Note that setting a new location (i.e: bounds.origin()) for a
diff --git a/ui/ozone/platform/wayland/host/wayland_window_manager.cc b/ui/ozone/platform/wayland/host/wayland_window_manager.cc
index 386c1bf..58dd75f 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_manager.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_manager.cc
@@ -229,4 +229,12 @@
   return result;
 }
 
+bool WaylandWindowManager::IsWindowValid(const WaylandWindow* window) const {
+  for (auto pair : window_map_) {
+    if (pair.second == window)
+      return true;
+  }
+  return false;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_window_manager.h b/ui/ozone/platform/wayland/host/wayland_window_manager.h
index 59a398a..4122bc76 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_manager.h
+++ b/ui/ozone/platform/wayland/host/wayland_window_manager.h
@@ -99,6 +99,9 @@
   // Returns all stored windows.
   std::vector<WaylandWindow*> GetAllWindows() const;
 
+  // Returns true if the |window| still exists.
+  bool IsWindowValid(const WaylandWindow* window) const;
+
   void AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window);
   void RemoveWindow(gfx::AcceleratedWidget widget);
   void AddSubsurface(gfx::AcceleratedWidget widget,
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index 74a8478..64d148e 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -2876,16 +2876,22 @@
 
     Sync();
 
-    constexpr uint32_t enter_serial = 1;
-    constexpr uint32_t button_press_serial = 2;
-    constexpr uint32_t button_release_serial = 3;
+    constexpr uint32_t keyboard_enter_serial = 1;
+    constexpr uint32_t pointer_enter_serial = 2;
+    constexpr uint32_t button_press_serial = 3;
+    constexpr uint32_t button_release_serial = 4;
 
     wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
         window_->root_surface()->GetSurfaceId());
     struct wl_array empty;
     wl_array_init(&empty);
-    wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial,
-                           toplevel_surface->resource(), &empty);
+    wl_keyboard_send_enter(server_.seat()->keyboard()->resource(),
+                           keyboard_enter_serial, toplevel_surface->resource(),
+                           &empty);
+
+    wl_pointer_send_enter(server_.seat()->pointer()->resource(),
+                          pointer_enter_serial, toplevel_surface->resource(),
+                          wl_fixed_from_int(0), wl_fixed_from_int(0));
 
     // Send two events - button down and button up.
     wl_pointer_send_button(server_.seat()->pointer()->resource(),
diff --git a/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc b/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
index df3e9e8..443118a0b 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
+++ b/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
@@ -320,15 +320,18 @@
   NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_SAFE, false);
 }
 
-IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, ShowsInterstitial_Malware) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
+                       DISABLED_ShowsInterstitial_Malware) {
   NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_MALWARE, true);
 }
 
-IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, ShowsInterstitial_Phishing) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
+                       DISABLED_ShowsInterstitial_Phishing) {
   NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_PHISHING, true);
 }
 
-IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, CheckNavigationErrorType) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
+                       DISABLED_CheckNavigationErrorType) {
   auto threat_types = {
       safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
       safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
@@ -356,7 +359,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
-                       ShowsInterstitial_Malware_Subresource) {
+                       DISABLED_ShowsInterstitial_Malware_Subresource) {
   NavigateWithSubResourceAndThreatType(
       safe_browsing::SB_THREAT_TYPE_URL_MALWARE, true);
 }